add files

This commit is contained in:
jianping.xwh
2019-01-08 17:47:25 +08:00
commit 49b86b4c70
68 changed files with 4429 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.idea/*
.DS_Store
.flutter-plugins
.packages
android/*
build/*
flutter_rookie_book.iml
flutter_rookie_book_android.iml
ios/*
pubspec.lock

160
README.md Normal file
View File

@ -0,0 +1,160 @@
# flutter-common-widgets-app
### 使用背景
* 鉴于目前flutter官方庞大的小部件widget系统以及api文档只有文字描述而没有可视化实例。
* 我们开发这套app,可以系统的看到常用小部件widget的用法。
* 辅助初学者更快上手,flutter官方小部件widget
### 参考资料
* [flutter-widgets的官方库地址]( https://docs.flutter.kim/widgets/widgets-library.html )
* [flutter-widgets的官方目录集]( http://doc.flutter-dev.cn/widgets/ )
* [sqlitestudio 本地可视化工具] (https://sqlitestudio.pl/index.rvt)
### 分支命名及使用规范
* 分支命名规范
- 自己开发分支命名统一为 username yifeng
- 分支两条主线为 Master分支和develop分支
- Master作为发布分支develop作为开发测试分支、自己开发分支从dev checkout出去发布即 merge to master
* 分支合并规范
- 从最新的develop分支checkout出自己的开发分支
- 在自己开发开发分支开发完成后先去develop分支pull最新代码
- 将develop 分支最新代码 merge 到自己分支,确保无冲突
- 再切回develop分支merge自己开发分支代码确保无冲突且能正常运行
### commit 提交规范
* $git cz
* 用于说明 commit 的类别只允许使用下面7个标识。
- feat新功能feature
- fix修补bug
- docs文档documentation
- style 格式(不影响代码运行的变动)
- refactor重构即不是新增功能也不是修改bug的代码变动
- test增加测试
- chore构建过程或辅助工具的变动
### 代码规范
* 文件命名规范
- 文件命名使用下划线命名法hello_world
- 请使用英文进行命名,不允许使用拼音。命名要求具有可读性,尽量避免使用缩写与数字
- 未完待续
* 代码编码规范
- 文件编码统一使用 UTF-8 编码;
- 前端编码采用首字母小写驼峰法. Widget Class 必须采用首字母大写驼峰法.
### 文件目录结构(以LIb文件说明)
- lib
- main.dart 入口文件
- common 公共的method
- components widget
- generated
- model 存放模型, 不应该加入逻辑层
- router 路由
- views 展示界面
- widget (与components概念重合,废弃)
``` javascript
├── main.dart //入口文件
├── common 公共的method
│   ├── Style.dart
│   ├── eventBus.dart
│   ├── provider.dart
│   └── sql.dart
├── components //app展示框架用到的组件
│   ├── Input.dart
│   ├── List.dart
│   ├── Pagination.dart
│   ├── Pagination2.dart
│   ├── SearchInput.dart
│   └── homeBanner.dart
├── generated
│   └── i18n.dart
├── model //本地存放模型, 不应该加入逻辑层
│   ├── base.dart
│   ├── cat.dart
│   ├── story.dart
│   └── widget.dart
├── routers //路由
│   ├── application.dart
│   ├── router_handler.dart
│   └── routers.dart
├── views //app展示界面
│   ├── Detail.dart
│   ├── FirstPage.dart
│   ├── FourthPage.dart
│   ├── ThirdPage.dart
│   ├── category.dart
│   ├── demos
│   │   ├── home.dart
│   │   └── layout
│   │   ├── SamplePage.dart
│   │   └── layout_type.dart
│   └── widgetPage.dart
└── widgets
└── ... //下面详细说明
```
``` javascript
└── widgets // 对flutter所有元素和组件的分类
├── 404.dart
├── index.dart // widgets 的总入口文件
├── components // 组件的分类 (区别于上面的components)
│   └── index.dart
├── elements // 基础元素的分类
│   ├── index.dart // elements下的 elements 类型入口文件
│   ├── Form // elements下的 From 类型集合
│   │   ├── Button // button 元素,里面是 文件夹代表类名/index.dart
│   │   │   ├── FlatButton
│   │   │   │   └── index.dart
│   │   │   ├── RaisedButton
│   │   │   │   └── index.dart
│   │   │   └── index.dart
│   │   ├── CheckBox
│   │   ├── Input
│   │   ├── Radio
│   │   ├── Slider
│   │   ├── Switch
│   │   ├── Text
│   │   └── index.dart
│   ├── Frame // elements下的 Frame 类型集合
│   │   ├── Align
│   │   ├── Axis
│   │   ├── Box
│   │   ├── Expanded
│   │   ├── Layout
│   │   ├── Stack
│   │   ├── Table
│   │   └── spacing
│   └── Media // elements下的 Media 类型集合
│      ├── Canvas
│      ├── Icon
│      └── Image
└── themes
└── index.dart
```
```javascript
widget 里的文件结构,用来存放封装的逻辑组件, 文件目录应为, 类比rax
- widget // widget 下详细元素或组件的目录结构
- hello-world // 例如
- mods // (可选, 子模块)
- mocks // (可选)
- utils // (可选, 存放暂时的私有method)
- schema
- index.dart
```

1964
api.md Normal file

File diff suppressed because it is too large Load Diff

BIN
assets/app.db Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

23
lib/common/Style.dart Normal file
View File

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
//颜色配置
class AppColor{
static const int white = 0xFFFFFFFF;
static const int mainTextColor = 0xFF121917;
static const int subTextColor = 0xff959595;
}
//文本设置
class AppText{
static const middleSize = 16.0;
static const middleText = TextStyle(
color: Color(AppColor.mainTextColor),
fontSize: middleSize,
);
static const middleSubText = TextStyle(
color: Color(AppColor.subTextColor),
fontSize: middleSize,
);
}

8
lib/common/eventBus.dart Normal file
View File

@ -0,0 +1,8 @@
import 'package:event_bus/event_bus.dart';
EventBus eventBus = new EventBus();
class MyEvent {
String text;
MyEvent(this.text);
}

32
lib/common/provider.dart Normal file
View File

@ -0,0 +1,32 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:flutter/services.dart' show rootBundle;
class Provider {
static Database db;
//初始化数据库
// isCreate 用永远 copy 一个新的数据库
Future init(bool isCreate) async {
String databasesPath = await getDatabasesPath();
String path = join(databasesPath,'flutter.db');
print("path ${path}");
if(db == null && isCreate){
ByteData data = await rootBundle.load(join("assets", "app.db"));
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await new File(path).writeAsBytes(bytes);
db = await openDatabase(path,version: 2,onCreate : (Database db, int version) async{
print('db created version is $version');
},onOpen : (Database db) async{
print('new db opened');
});
}else{
print('Opening existing database');
}
}
}

66
lib/common/sql.dart Normal file
View File

@ -0,0 +1,66 @@
import './provider.dart';
import 'dart:async';
import 'package:sqflite/sqflite.dart';
class BaseModel{
Database db;
final String table = '';
var query;
BaseModel(this.db){
query = db.query;
}
}
class Sql extends BaseModel {
final String tableName;
Sql.setTable(String name)
: tableName = name,
super(Provider.db);
// sdf
Future<List> get() async{
return await this.query(tableName);
}
String getTableName () {
return tableName;
}
// condition: {}
Future<List> getByCondition({Map<dynamic, dynamic> conditions}) async {
if (conditions == null || conditions.isEmpty) {
return this.get();
}
String stringConditions = '';
int index = 0;
print("condition>>> $conditions");
conditions.forEach((key, value) {
if (value == null) {
return ;
}
print("$key value.runtimeType: ${value.runtimeType}");
if (value.runtimeType == String) {
stringConditions = '$stringConditions $key = "$value"';
}
if (value.runtimeType == int) {
stringConditions = '$stringConditions $key = $value';
}
if (index >= 0 && index < conditions.length -1) {
stringConditions = '$stringConditions and';
}
index++;
});
print("this is string condition for sql > $stringConditions");
return await this.query(tableName, where: stringConditions);
}
Future<Map<String, dynamic>> insert(Map<String, dynamic> json) async {
var id = await this.db.insert(tableName, json);
json['id'] = id;
return json;
}
}

26
lib/components/Input.dart Normal file
View File

@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class Input extends StatelessWidget {
Input({Key key, this.active}):super(key: key);
String active;
TextEditingController controller;
@override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new Container(
padding: new EdgeInsets.only(top:100.0),
child: new Text('这是一个组件')
),
new Container(
decoration: new BoxDecoration(border: new Border.all(width:1.0,color: Colors.blue)),
padding: new EdgeInsets.all(20.0),
child: new Text('来自输入框:'+active)
)
],
);
}
}

100
lib/components/List.dart Normal file
View File

@ -0,0 +1,100 @@
import 'package:flutter/material.dart';
import 'package:flutter_rookie_book/views/Detail.dart';
class List extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new ListState();
}
}
class ListState extends State<List> {
@override
Widget build(BuildContext context) {
/// getData() ; this is test;
return new ListView.builder(
//itemCount: data == null ? 0 : data.length,
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return new Card(
/////child: new Container(
/////padding: new EdgeInsets.all(10.0),
child: new ListTile(
subtitle: new Container(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
new Expanded(
child: new Text("Title",
///data[index]["title"],
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0)),
)
],
),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Text("time:"),
new Text("2018-05-06")
],
),
],
),
new Row(
children: <Widget>[
new Container(
padding:
const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 2.0),
child: new Text("content"),
///child: new Text("id:"+data[index]["id"].toString()),
),
],
),
],
),
),
trailing: new Icon(
Icons.keyboard_arrow_right,
color: Colors.grey,
),
///onTap: () => _onTap(data[index]["id"].toString()),
onTap: () => _onTap('1'),
),
/////),
);
},
);
}
void _onTap(String id){
Navigator.of(context).push(new PageRouteBuilder(
opaque: false,
pageBuilder: (BuildContext context, _,__){
return new Detail(id);
},
transitionsBuilder: (_,Animation<double> animation,__,Widget child){
return new FadeTransition(
opacity: animation,
child: new SlideTransition(position: new Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),child: child,
),
) ;
}
));
}
}

View File

@ -0,0 +1,68 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import './homeBanner.dart';
import '../model/story.dart';
class Pagination extends StatelessWidget {
static const String routeName = '/material/page-selector';
static final List<Icon> icons = <Icon>[
const Icon(Icons.event, semanticLabel: 'Event'),
const Icon(Icons.home, semanticLabel: 'Home'),
const Icon(Icons.android, semanticLabel: 'Android'),
const Icon(Icons.alarm, semanticLabel: 'Alarm'),
const Icon(Icons.face, semanticLabel: 'Face'),
const Icon(Icons.language, semanticLabel: 'Language'),
];
List<StoryModel> bannerStories = [];
List<dynamic> arr = [
{'image': 'https://pic2.zhimg.com/v2-6733af287e1220a041fc8dcef6be9dc9.jpg', 'type': 0, 'id': 9695909, 'ga_prefix': 091507, 'title': '从一个摄影师的角度,我来谈谈这一代 iPhone 是如何继续'},
{'image': 'https://pic1.zhimg.com/v2-c9a673ff89e5cc4c31ffcdf0c2d2f364.jpg', 'type': 0, 'id': 9695859, 'ga_prefix': 091517, 'title': '怪不得他能拿诺贝尔文学奖,一个月就能写出一部了不起的长篇小说'},
{'image': 'https://pic3.zhimg.com/v2-cf89ac60dbe59ab3ca908ff4bbf843e6.jpg', 'type': 0, 'id': 96956491409, 'title': '「死者被碎尸,警方排除他杀」,这并不荒唐'},
{'image': 'https://pic4.zhimg.com/v2-e21659cb7bc4ab43599772fa552e6e8b.jpg', 'type': 0, 'id': 9695816, 'ga_prefix': 091312, 'title': 'iPhone 5c、SE 接连失利之后,这次苹果还是「没长记性」'}
];
// @override
// void initState() { // 无状态widget 不会调用
// print('start');
// /// super.initState();
// arr.forEach((item) {
// bannerStories.add(StoryModel.fromJson(item));
// });
//
// }
List<Widget> _PageSelector(BuildContext context) {
List<Widget> list = [];
print('start');
/// super.initState();
arr.forEach((item) {
bannerStories.add(StoryModel.fromJson(item));
});
if (icons.length > 0) {
list.add(HomeBanner(bannerStories, (story) {
/// _openStoryDetailPage(story);
}));
}
return list;
}
@override
Widget build(BuildContext context) {
return
Column(
//physics: AlwaysScrollableScrollPhysics(),
//padding: EdgeInsets.only(),
children: _PageSelector(context)
);
}
}

View File

@ -0,0 +1,112 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
class _PageSelector extends StatelessWidget {
const _PageSelector({ this.icons });
final List<Icon> icons;
void _handleArrowButtonPress(BuildContext context, int delta) {
final TabController controller = DefaultTabController.of(context);
if (!controller.indexIsChanging)
controller.animateTo((controller.index + delta).clamp(0, icons.length - 1));
}
@override
Widget build(BuildContext context) {
final TabController controller = DefaultTabController.of(context);
final Color color = Theme.of(context).accentColor;
return new SafeArea(
top: false,
bottom: false,
child: new Column(
children: <Widget>[
new Expanded(
child: new IconTheme(
data: new IconThemeData(
size: 128.0,
color: color,
),
child: new TabBarView(
children: icons.map((Icon icon) {
return
new Align(
alignment: Alignment.topCenter,
child:new Container(
color: Colors.red,
width:300.0,
height:250.0,
padding: const EdgeInsets.all(12.0),
child: new Card(
color: Colors.yellow,
child: new Center(
child: icon,
),
),
)
);
}).toList()
),
),
),
new Container(
margin: const EdgeInsets.only(top: 16.0),
child: new Row(
children: <Widget>[
new IconButton(
icon: const Icon(Icons.chevron_left),
color: color,
onPressed: () { _handleArrowButtonPress(context, -1); },
tooltip: 'Page back'
),
new TabPageSelector(controller: controller),
new IconButton(
icon: const Icon(Icons.chevron_right),
color: color,
onPressed: () { _handleArrowButtonPress(context, 1); },
tooltip: 'Page forward'
)
],
mainAxisAlignment: MainAxisAlignment.spaceBetween
)
),
],
),
);
}
}
class Pagination2 extends StatelessWidget {
static const String routeName = '/material/page-selector';
static final List<Icon> icons = <Icon>[
const Icon(Icons.event, semanticLabel: 'Event'),
const Icon(Icons.home, semanticLabel: 'Home'),
const Icon(Icons.android, semanticLabel: 'Android'),
const Icon(Icons.alarm, semanticLabel: 'Alarm'),
const Icon(Icons.face, semanticLabel: 'Face'),
const Icon(Icons.language, semanticLabel: 'Language'),
];
// @override
// Widget build(BuildContext context) {
// return new Scaffold(
// appBar: new AppBar(title: const Text('Page selector')),
// body: new DefaultTabController(
// length: icons.length,
// child: new _PageSelector(icons: icons),
// ),
// );
// }
@override
Widget build(BuildContext context) {
return new DefaultTabController(
length: icons.length,
child: new _PageSelector(icons: icons),
);
}
}

View File

@ -0,0 +1,372 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../common/Style.dart';
import 'package:meta/meta.dart';
typedef String FormFieldFormatter<T>(T v);
typedef bool MaterialSearchFilter<T>(T v, String c);
typedef int MaterialSearchSort<T>(T a, T b, String c);
typedef Future<List<MaterialSearchResult>> MaterialResultsFinder(String c);
typedef void OnSubmit(String value);
///搜索结果内容显示面板
class MaterialSearchResult<T> extends StatelessWidget {
const MaterialSearchResult({
Key key,
this.value,
this.text,
this.icon,
}) : super(key: key);
final T value;
final String text;
final IconData icon;
@override
Widget build(BuildContext context) {
return new Container(
child: new Row(
children: <Widget>[
new Container(width: 30.0, child: new Icon(icon)),
new Expanded(child: new Text(text, style: Theme.of(context).textTheme.subhead)),
],
),
height: 64.0,
);
}
}
class MaterialSearch<T> extends StatefulWidget {
MaterialSearch({
Key key,
this.placeholder,
this.results,
this.getResults,
this.filter,
this.sort,
this.limit: 10,
this.onSelect,
this.onSubmit,
this.barBackgroundColor = Colors.white,
this.iconColor = Colors.black,
this.leading,
}) : assert(() {
if (results == null && getResults == null
|| results != null && getResults != null) {
throw new AssertionError('Either provide a function to get the results, or the results.');
}
return true;
}()),
super(key: key);
final String placeholder;
final List<MaterialSearchResult<T>> results;
final MaterialResultsFinder getResults;
final MaterialSearchFilter<T> filter;
final MaterialSearchSort<T> sort;
final int limit;
final ValueChanged<T> onSelect;
final OnSubmit onSubmit;
final Color barBackgroundColor;
final Color iconColor;
final Widget leading;
@override
_MaterialSearchState<T> createState() => new _MaterialSearchState<T>();
}
class _MaterialSearchState<T> extends State<MaterialSearch> {
bool _loading = false;
List<MaterialSearchResult<T>> _results = [];
String _criteria = '';
TextEditingController _controller = new TextEditingController();
_filter(dynamic v, String c) {
return v.toString().toLowerCase().trim()
.contains(new RegExp(r'' + c.toLowerCase().trim() + ''));
}
@override
void initState() {
super.initState();
if (widget.getResults != null) {
_getResultsDebounced();
}
_controller.addListener(() {
setState(() {
_criteria = _controller.value.text;
if (widget.getResults != null) {
_getResultsDebounced();
}
});
});
}
Timer _resultsTimer;
Future _getResultsDebounced() async {
if (_results.length == 0) {
setState(() {
_loading = true;
});
}
if (_resultsTimer != null && _resultsTimer.isActive) {
_resultsTimer.cancel();
}
_resultsTimer = new Timer(new Duration(milliseconds: 400), () async {
if (!mounted) {
return;
}
setState(() {
_loading = true;
});
//TODO: debounce widget.results too
var results = await widget.getResults(_criteria);
if (!mounted) {
return;
}
if(results != null){
setState(() {
_loading = false;
_results = results;
});
}
});
}
@override
void dispose() {
super.dispose();
_resultsTimer?.cancel();
}
@override
Widget build(BuildContext context) {
var results = (widget.results ?? _results)
.where((MaterialSearchResult result) {
if (widget.filter != null) {
return widget.filter(result.value, _criteria);
}
//only apply default filter if used the `results` option
//because getResults may already have applied some filter if `filter` option was omited.
else if (widget.results != null) {
return _filter(result.value, _criteria);
}
return true;
})
.toList();
if (widget.sort != null) {
results.sort((a, b) => widget.sort(a.value, b.value, _criteria));
}
results = results
.take(widget.limit)
.toList();
IconThemeData iconTheme = Theme.of(context).iconTheme.copyWith(color: widget.iconColor);
return new Scaffold(
appBar: new AppBar(
leading: widget.leading,
backgroundColor: widget.barBackgroundColor,
iconTheme: iconTheme,
title: new TextField(
controller: _controller,
autofocus: true,
decoration: new InputDecoration.collapsed(hintText: widget.placeholder),
style: Theme.of(context).textTheme.title,
onSubmitted: (String value) {
if (widget.onSubmit != null) {
widget.onSubmit(value);
}
},
),
actions: _criteria.length == 0 ? [] : [
new IconButton(
icon: new Icon(Icons.clear),
onPressed: () {
setState(() {
_controller.text = _criteria = '';
});
}
),
],
),
body: _loading
? new Center(
child: new Padding(
padding: const EdgeInsets.only(top: 50.0),
child: new CircularProgressIndicator()
),
)
: new SingleChildScrollView(
child: new Column(
children: results.map((MaterialSearchResult result) {
return new InkWell(
onTap: () => widget.onSelect(result.value),
child: result,
);
}).toList(),
),
),
);
}
}
class _MaterialSearchPageRoute<T> extends MaterialPageRoute<T> {
_MaterialSearchPageRoute({
@required WidgetBuilder builder,
RouteSettings settings: const RouteSettings(),
maintainState: true,
bool fullscreenDialog: false,
}) : assert(builder != null),
super(builder: builder, settings: settings, maintainState: maintainState, fullscreenDialog: fullscreenDialog);
}
class MaterialSearchInput<T> extends StatefulWidget {
MaterialSearchInput({
Key key,
this.onSaved,
this.validator,
this.autovalidate,
this.placeholder,
this.formatter,
this.results,
this.getResults,
this.filter,
this.sort,
this.onSelect,
});
final FormFieldSetter<T> onSaved;
final FormFieldValidator<T> validator;
final bool autovalidate;
final String placeholder;
final FormFieldFormatter<T> formatter;
final List<MaterialSearchResult<T>> results;
final MaterialResultsFinder getResults;
final MaterialSearchFilter<T> filter;
final MaterialSearchSort<T> sort;
final ValueChanged<T> onSelect;
@override
_MaterialSearchInputState<T> createState() => new _MaterialSearchInputState<T>();
}
class _MaterialSearchInputState<T> extends State<MaterialSearchInput<T>> {
GlobalKey<FormFieldState<T>> _formFieldKey = new GlobalKey<FormFieldState<T>>();
_buildMaterialSearchPage(BuildContext context) {
return new _MaterialSearchPageRoute<T>(
settings: new RouteSettings(
name: 'material_search',
isInitialRoute: false,
),
builder: (BuildContext context) {
return new Material(
child: new MaterialSearch<T>(
placeholder: widget.placeholder,
results: widget.results,
getResults: widget.getResults,
filter: widget.filter,
sort: widget.sort,
onSelect: (dynamic value) => Navigator.of(context).pop(value),
),
);
}
);
}
_showMaterialSearch(BuildContext context) {
Navigator.of(context)
.push(_buildMaterialSearchPage(context))
.then((dynamic value) {
if (value != null) {
_formFieldKey.currentState.didChange(value);
widget.onSelect(value);
}
});
}
bool get autovalidate {
return widget.autovalidate ?? Form.of(context)?.widget?.autovalidate ?? false;
}
bool _isEmpty(field) {
return field.value == null;
}
Widget build(BuildContext context) {
final TextStyle valueStyle = Theme.of(context).textTheme.subhead;
return new InkWell(
onTap: () => _showMaterialSearch(context),
child: new FormField<T>(
key: _formFieldKey,
validator: widget.validator,
onSaved: widget.onSaved,
autovalidate: autovalidate,
builder: (FormFieldState<T> field) {
return new InputDecorator(
isEmpty: _isEmpty(field),
decoration: new InputDecoration(
labelText: widget.placeholder,
errorText: field.errorText,
),
child: _isEmpty(field) ? null : new Text(
widget.formatter != null
? widget.formatter(field.value)
: field.value.toString(),
style: valueStyle
),
);
},
),
);
}
}
///搜索框
class SearchInput extends StatelessWidget {
final getResults;
final ValueChanged<String> onSubmitted;
final VoidCallback onSubmitPressed;
SearchInput(this.getResults, this.onSubmitted, this.onSubmitPressed);
@override
Widget build(BuildContext context) {
return new Container(
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.only(right: 10.0, top: 3.0),
child: new Icon(Icons.search, size: 24.0, color: Theme.of(context).primaryColorDark),
),
new Expanded(
child: new MaterialSearchInput(
placeholder: '搜索 flutter 组件',
getResults: getResults,
)
),
],
),
);
}
}
// wigdet干掉.=> componets

View File

@ -0,0 +1,140 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../model/story.dart';
class HomeBanner extends StatefulWidget {
final List<StoryModel> bannerStories;
final OnTapBannerItem onTap;
HomeBanner(this.bannerStories, this.onTap, {Key key})
:super(key: key);
@override
State<StatefulWidget> createState() {
return _BannerState();
}
}
class _BannerState extends State<HomeBanner> {
int virtualIndex = 0;
int realIndex = 1;
PageController controller;
Timer timer;
@override
void initState() {
super.initState();
controller = PageController(initialPage: realIndex);
timer = Timer.periodic(Duration(seconds: 5), (timer) { // 自动滚动
/// print(realIndex);
controller.animateToPage(realIndex + 1,
duration: Duration(milliseconds: 300),
curve: Curves.linear);
});
}
@override
void dispose() {
super.dispose();
controller.dispose();
timer.cancel();
}
@override
Widget build(BuildContext context) {
return Container(
height: 226.0,
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
PageView(
controller: controller,
onPageChanged: _onPageChanged,
children: _buildItems(),),
_buildIndicator(), // 下面的小点
]),
);
}
List<Widget> _buildItems() { // 排列轮播数组
List<Widget> items = [];
if (widget.bannerStories.length > 0) {
// 头部添加一个尾部Item模拟循环
items.add(
_buildItem(widget.bannerStories[widget.bannerStories.length - 1]));
// 正常添加Item
items.addAll(
widget.bannerStories.map((story) => _buildItem(story)).toList(
growable: false));
// 尾部
items.add(
_buildItem(widget.bannerStories[0]));
}
return items;
}
Widget _buildItem(StoryModel story) {
return GestureDetector(
onTap: () { // 按下
if (widget.onTap != null) {
widget.onTap(story);
}
},
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Image.network(story.image, fit: BoxFit.cover),
_buildItemTitle(story.title), // 内容文字,大意
],),);
}
Widget _buildItemTitle(String title) {
return Container(
decoration: BoxDecoration( /// 背景的渐变色
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: const Alignment(0.0, -0.8),
colors: [const Color(0xa0000000), Colors.transparent],
),
),
alignment: Alignment.bottomCenter,
child: Container(
margin: EdgeInsets.symmetric(vertical: 22.0, horizontal: 16.0),
child: Text(
title, style: TextStyle(color: Colors.white, fontSize: 22.0),),),
);
}
Widget _buildIndicator() {
List<Widget> indicators = [];
for (int i = 0; i < widget.bannerStories.length; i++) {
indicators.add(Container(
width: 6.0,
height: 6.0,
margin: EdgeInsets.symmetric(horizontal: 1.5, vertical: 10.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: i == virtualIndex ? Colors.white : Colors.grey)));
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: indicators);
}
_onPageChanged(int index) {
realIndex = index;
int count = widget.bannerStories.length;
if (index == 0) {
virtualIndex = count - 1;
controller.jumpToPage(count);
} else if (index == count + 1) {
virtualIndex = 0;
controller.jumpToPage(1);
} else {
virtualIndex = index - 1;
}
setState(() {});
}
}
typedef void OnTapBannerItem(StoryModel story);

78
lib/generated/i18n.dart Normal file
View File

@ -0,0 +1,78 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: camel_case_types
// ignore_for_file: prefer_single_quotes
//This file is automatically generated. DO NOT EDIT, all your changes would be lost.
class S implements WidgetsLocalizations {
const S();
static const GeneratedLocalizationsDelegate delegate =
const GeneratedLocalizationsDelegate();
static S of(BuildContext context) =>
Localizations.of<S>(context, WidgetsLocalizations);
@override
TextDirection get textDirection => TextDirection.ltr;
}
class en extends S {
const en();
}
class GeneratedLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
const GeneratedLocalizationsDelegate();
List<Locale> get supportedLocales {
return const <Locale>[
const Locale("en", ""),
];
}
LocaleResolutionCallback resolution({Locale fallback}) {
return (Locale locale, Iterable<Locale> supported) {
final Locale languageLocale = new Locale(locale.languageCode, "");
if (supported.contains(locale))
return locale;
else if (supported.contains(languageLocale))
return languageLocale;
else {
final Locale fallbackLocale = fallback ?? supported.first;
return fallbackLocale;
}
};
}
@override
Future<WidgetsLocalizations> load(Locale locale) {
final String lang = getLang(locale);
switch (lang) {
case "en":
return new SynchronousFuture<WidgetsLocalizations>(const en());
default:
return new SynchronousFuture<WidgetsLocalizations>(const S());
}
}
@override
bool isSupported(Locale locale) => supportedLocales.contains(locale);
@override
bool shouldReload(GeneratedLocalizationsDelegate old) => false;
}
String getLang(Locale l) => l.countryCode != null && l.countryCode.isEmpty
? l.languageCode
: l.toString();

150
lib/main.dart Normal file
View File

@ -0,0 +1,150 @@
import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';
import 'package:flutter/rendering.dart';
import 'views/FirstPage.dart';
import 'views/widgetPage.dart';
import 'views/ThirdPage.dart';
import 'views/FourthPage.dart';
import 'routers/routers.dart';
import 'routers/application.dart';
import 'common/provider.dart';
import 'model/widget.dart';
import 'package:flutter_rookie_book/components/SearchInput.dart';
import 'common/Style.dart';
class MyApp extends StatelessWidget {
MyApp() {
final router = new Router();
Routes.configureRoutes(router);
Application.router = router;
}
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'title',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
onGenerateRoute: Application.router.generator,
);
}
}
var db;
void main() async{
final provider = new Provider();
await provider.init(true);
db = Provider.db;
runApp(new MyApp());
}
class MyHomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
TabController controller;
bool isSearch = false;
String data = '';
String data2ThirdPage = '这是传给ThirdPage的值';
String appBarTitle = tabData[0]['text'];
static List tabData = [
{'text': '业界动态', 'icon': new Icon(Icons.language)},
{'text': 'WIDGET', 'icon': new Icon(Icons.extension)},
{'text': '官网地址', 'icon': new Icon(Icons.home)},
{'text': '关于手册', 'icon': new Icon(Icons.favorite)}
];
List<Widget> myTabs = [];
@override
void initState() {
// TODO: implement initState
super.initState();
controller = new TabController(
initialIndex: 0, vsync: this, length: 4); // 这里的length 决定有多少个底导 submenus
for (int i = 0; i < tabData.length; i++) {
myTabs.add(new Tab(text: tabData[i]['text'], icon: tabData[i]['icon']));
}
controller.addListener(() {
if (controller.indexIsChanging) {
_onTabChange();
}
});
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
Widget buildSearchInput(){
return new SearchInput((value) async{
return null;
// if(value != ''){
// widgetModel = new WidgetModel(db);
// List<Map> list = await widgetModel.search(value);
// print('list $list');
// return list.map((item) => new MaterialSearchResult<String>(
// value: item['name'],
// text: item['name'] + ' ' + item['cnName'],
// )).toList();
// }else{
// return null;
// }
},(value){},(){});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
// backgroundColor: new Color(AppColor.white),
title: buildSearchInput()),
body: new TabBarView(controller: controller, children: <Widget>[
new FirstPage(),
new WidgetPage(db),
new ThirdPage(
data2ThirdPage: data2ThirdPage,
callback: (val) => _onDataChange(val)),
new FourthPage()
]),
bottomNavigationBar: new Material(
color: const Color(0xFFF0EEEF), //底部导航栏主题颜色
child: new Container(
height: 65.0,
child: new TabBar(
controller: controller,
indicatorColor: Colors.blue, //tab标签的下划线颜色
labelColor: const Color(0xFF000000),
tabs: <Tab>[
new Tab(text: '业界动态', icon: new Icon(Icons.language)),
new Tab(text: '组件', icon: new Icon(Icons.extension)),
new Tab(text: '官网地址', icon: new Icon(Icons.home)),
new Tab(text: '关于手册', icon: new Icon(Icons.favorite)),
]))));
}
void _onTabChange() {
this.setState(() {
appBarTitle = tabData[controller.index]['text'];
});
}
void _onDataChange(val) {
setState(() {
data = val;
});
}
}

11
lib/model/base.dart Normal file
View File

@ -0,0 +1,11 @@
import 'package:sqflite/sqflite.dart';
import 'dart:async';
class BaseModel{
Database db;
final String table = '';
var query;
BaseModel(this.db){
query = db.query;
}
}

108
lib/model/cat.dart Normal file
View File

@ -0,0 +1,108 @@
import 'base.dart';
import 'dart:async';
import 'dart:convert';
import '../common/sql.dart';
abstract class CatInterface{
int get id;
//类目名称
String get name;
//描述
String get desc;
//第几级类目,默认 1
int get depth;
//父类目id没有为 0
int get parentId;
}
class Cat implements CatInterface {
int id;
String name;
String desc;
int depth;
int parentId;
Cat({this.id, this.name, this.desc, this.depth, this.parentId});
Cat.fromJSON(Map json)
: id = json['id'],
name = json['name'],
desc = json['desc'],
depth = json['depth'],
parentId = json['parentId'];
String toString() {
return '(Cat $name)';
}
Map toMap() {
return {
'id': id,
'name': name,
'desc': desc,
'depth': depth,
'parentId': parentId
};
}
Map toSqlCondition() {
Map _map = this.toMap();
Map condition = {};
_map.forEach((k, value) {
if (value != null) {
condition[k] = value;
}
});
if (condition.isEmpty) {
return {};
}
return condition;
}
}
class CatControlModel{
final String table = 'cat';
Sql sql;
CatControlModel() {
sql = Sql.setTable(table);
}
/// 获取一级类目
Future<List> mainList() async{
List listJson = await sql.getByCondition(conditions: {'parentId': 0});
List<Cat> cats = listJson.map((json) {
return new Cat.fromJSON(json);
}).toList();
return cats;
}
// 获取Cat不同深度与parent的列表
Future<List<Cat>> getList([Cat cat]) async{
if (cat == null) {
cat = new Cat(depth: 1, parentId: 0);
}
print("cat in getList ${cat.toMap()}");
List listJson = await sql.getByCondition(conditions: cat.toSqlCondition());
List<Cat> cats = listJson.map((json) {
return new Cat.fromJSON(json);
}).toList();
return cats;
}
// 通过name获取Cat对象信息
Future<Cat> getCatByName(String name) async {
List json = await sql.getByCondition(conditions: {'name': name});
if (json.isEmpty) {
return null;
}
return new Cat.fromJSON(json.first);
}
}

21
lib/model/story.dart Normal file
View File

@ -0,0 +1,21 @@
class StoryModel {
final String title;
final String image;
final int id;
StoryModel(this.id, this.title, {this.image});
StoryModel.fromJson(Map<String, dynamic> json)
: this(json['id'], json['title'],
image: json['image'] != null ? json['image'] : (json['images'] != null
? json['images'][0]
: null));
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'image': image
};
}
}

112
lib/model/widget.dart Normal file
View File

@ -0,0 +1,112 @@
import 'base.dart';
import 'dart:async';
import '../common/sql.dart';
import "package:flutter/material.dart";
abstract class WidgetInterface{
int get id;
//组件英文名
String get name;
//组件中文名
String get cnName;
//组件截图
String get image;
//组件markdown 文档
String get doc;
//类目 id
int get catId;
}
class WidgetPoint implements WidgetInterface{
int id;
//组件英文名
String name;
//组件中文名
String cnName;
//组件截图
String image;
// 路由地址
String routerName;
//组件markdown 文档
String doc;
//组件 demo ,多个以 , 分割
String demo;
//类目 id
int catId;
final WidgetBuilder buildRouter;
WidgetPoint({
this.id,
this.name,
this.cnName,
this.image,
this.doc,
this.catId,
this.routerName,
this.buildRouter
});
WidgetPoint.fromJSON(Map json)
: id = json['id'],
name = json['name'],
image = json['image'],
cnName = json['cnName'],
routerName = json['routerName'],
doc = json['doc'],
catId = json['catId'],
buildRouter = json['buildRouter'];
String toString() {
return '(WidgetPoint $name)';
}
Object toMap() {
return {
'id': id,
'name': name,
'cnName': cnName,
'image': image,
'doc': doc,
'catId': catId
};
}
Map toSqlCondition() {
Map _map = this.toMap();
Map condition = {};
_map.forEach((k, value) {
if (value != null) {
condition[k] = value;
}
});
if (condition.isEmpty) {
return {};
}
return condition;
}
}
class WidgetControlModel{
final String table = 'widget';
Sql sql;
WidgetControlModel() {
sql = Sql.setTable(table);
}
// 获取Widget不同条件的列表
Future<List<WidgetPoint>> getList(WidgetPoint widgetPoint) async{
List listJson = await sql.getByCondition(conditions: widgetPoint.toSqlCondition());
List<WidgetPoint> widgets = listJson.map((json) {
return new WidgetPoint.fromJSON(json);
}).toList();
print("widgets $widgets");
return widgets;
}
// 通过name获取Cat对象信息
Future<WidgetPoint> getCatByName(String name) async {
List json = await sql.getByCondition(conditions: {'name': name});
if (json.isEmpty) {
return null;
}
return new WidgetPoint.fromJSON(json.first);
}
}

View File

@ -0,0 +1,5 @@
import 'package:fluro/fluro.dart';
class Application {
static Router router;
}

View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';
import '../views/category.dart';
import '../widgets/404.dart';
var categoryHandler = new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
print("params $params");
String name = params["type"]?.first;
print("type::: $name");
return new CategoryHome(name);
},
);
var widgetNotFoundHandler = new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return new WidgetNotFound();
}
);

30
lib/routers/routers.dart Normal file
View File

@ -0,0 +1,30 @@
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import '../widgets/index.dart';
import '../model/widget.dart';
import './router_handler.dart';
class Routes {
static String root = "/";
static void configureRoutes(Router router) {
List widgetDemosList = new WidgetDemoList().getDemos();
router.notFoundHandler = new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
print("ROUTE WAS NOT FOUND !!!");
});
router.define('/category/:type', handler: categoryHandler);
router.define('/category/error/404', handler: widgetNotFoundHandler);
widgetDemosList.forEach((demo) {
Handler handler = new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return demo.buildRouter(context);
});
router.define('${demo.routerName}', handler: handler);
});
}
}

22
lib/views/Detail.dart Normal file
View File

@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
class Detail extends StatelessWidget {
final String id ;
Detail(this.id) ;
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('List Detail'),
),
body: new Center(
child: new Text('msg:'+ 'id='+id),
),
) ;
}
}

38
lib/views/FirstPage.dart Normal file
View File

@ -0,0 +1,38 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_rookie_book/components/List.dart';
import 'package:flutter_rookie_book/components/Pagination.dart';
import '../common/sql.dart';
import 'dart:async';
class FirstPage extends StatefulWidget {
@override
FirstPageState createState() => new FirstPageState();
}
class FirstPageState extends State<FirstPage> {
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new Container(
child: new Pagination(),
),
new Expanded(
child: new List(),
),
]
);
}
}

22
lib/views/FourthPage.dart Normal file
View File

@ -0,0 +1,22 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../components/List.dart';
class FourthPage extends StatefulWidget {
@override
FourthPageState createState() => new FourthPageState();
}
class FourthPageState extends State<FourthPage> {
@override
Widget build(BuildContext context) {
return new Container(
child: new List()
);
}
}

69
lib/views/ThirdPage.dart Normal file
View File

@ -0,0 +1,69 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../common/eventBus.dart';
class ThirdPage extends StatefulWidget {
ThirdPage({Key key, this.data2ThirdPage, this.callback}) : super(key: key);
final callback;
String data2ThirdPage;
@override
ThirdPageState createState() => new ThirdPageState();
}
class ThirdPageState extends State<ThirdPage> {
String data = '';
String inputTxt;
TextEditingController controller = new TextEditingController();
void initState() {
//print('data4Two' + widget.data2ThirdPage);
data = widget.data2ThirdPage;
}
void firedA() {
widget.callback('$inputTxt');
}
void firedB() {
eventBus.fire(new MyEvent('$inputTxt'));
}
void _onChanged(String value) {
setState(() {
inputTxt = value;
});
}
@override
Widget build(BuildContext context) {
return new Center(
child: new Column(children: <Widget>[
new Container(
padding: new EdgeInsets.only(bottom: 15.0),
child: new Text('子组件2'),
),
new Container(
padding: new EdgeInsets.only(bottom: 10.0),
child: new Text('父传子:' + data),
),
new Container(
margin: new EdgeInsets.only(bottom: 40.0),
child: new TextField(
controller: controller,
onChanged: _onChanged,
decoration: (new InputDecoration(labelText: '请输入你要发送的值')))),
new Container(
child: new RaisedButton(
onPressed: firedA, child: new Text('to父组件'))),
new Container(
child: new RaisedButton(
onPressed: firedB, child: new Text('to兄弟组件')))
]
)
);
}
}

251
lib/views/category.dart Normal file
View File

@ -0,0 +1,251 @@
import 'package:flutter/material.dart';
import '../routers/application.dart';
import '../model/cat.dart';
import '../model/widget.dart';
import '../widgets/index.dart';
enum CateOrWigdet {
Cat,
WidgetDemo
}
class CategoryHome extends StatefulWidget {
CategoryHome(this.name);
final String name;
@override
_CategoryHome createState() => new _CategoryHome();
}
class _CategoryHome extends State<CategoryHome> {
String title = '';
// 显示列表 cat or widget;
List<Cat> categories = [];
List<WidgetPoint> widgetPoints = [];
List<Cat> catHistory = new List();
CatControlModel catControl = new CatControlModel();
WidgetControlModel widgetControl = new WidgetControlModel();
// 所有的可用demos;
List widgetDemosList = new WidgetDemoList().getDemos();
@override
void initState() {
super.initState();
// 初始化加入顶级的name
this.getCatByName(widget.name).then((Cat cat) {
catHistory.add(cat);
searchCatOrWigdet();
});
}
Future<Cat> getCatByName(String name) async {
return await catControl.getCatByName(name);
}
Future<bool> back() {
if (catHistory.length == 1) {
return Future<bool>.value(true);
}
catHistory.removeLast();
searchCatOrWigdet();
return Future<bool>.value(false);
}
void go(Cat cat) {
catHistory.add(cat);
searchCatOrWigdet();
}
void searchCatOrWigdet() async {
// 假设进入这个界面的parent一定存在
Cat parentCat = catHistory.last;
int depth = catHistory.length;
// 继续搜索显示下一级depth: depth + 1, parentId: parentCat.id
List<Cat> _categories = await catControl.getList(new Cat(parentId: parentCat.id, depth: depth + 1));
List<WidgetPoint> _widgetPoints = new List();
if (_categories.isEmpty) {
_widgetPoints = await widgetControl.getList(new WidgetPoint(catId: parentCat.id));
}
this.setState(() {
categories = _categories;
title = parentCat.name;
widgetPoints = _widgetPoints;
});
}
void onCatgoryTap(Cat cat) {
go(cat);
}
void onWidgetTap(WidgetPoint widgetPoint) {
String targetName = widgetPoint.name;
String targetRouter = '/category/error/404';
widgetDemosList.forEach((item) {
if (item.name == targetName) {
targetRouter = item.routerName;
}
});
print("router> ${targetRouter}");
Application.router.navigateTo(context, "${targetRouter}");
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: WillPopScope(
onWillPop: () {
return back();
},
child: new Container(
child: new CategoryOrWidgetList(
categorys: categories,
widgetPoints: widgetPoints,
onCatgoryTap: onCatgoryTap,
onWidgetTap: onWidgetTap
),
)
)
);
}
}
class CategoryOrWidgetList extends StatelessWidget {
List<Cat> categorys = [];
List<WidgetPoint> widgetPoints = [];
var onCatgoryTap;
var onWidgetTap;
CategoryOrWidgetList({
this.categorys,
this.widgetPoints,
this.onCatgoryTap,
this.onWidgetTap,
});
Widget build(BuildContext context) {
print("categorys $categorys");
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, //每行2个
mainAxisSpacing: 0.0, //主轴(竖直)方向间距
crossAxisSpacing: 0.0, //纵轴(水平)方向间距
childAspectRatio: 0.8 //纵轴缩放比例
),
itemCount: widgetPoints.length == 0 ? categorys.length : widgetPoints.length,
itemBuilder: (BuildContext context, int index) {
if (widgetPoints.length > 0) {
return new ListItemWidget(
widgetPoint: widgetPoints[index],
onTap: () {
onWidgetTap(widgetPoints[index]);
},
);
}
return new ListCatWidget(
cat: categorys[index],
onTap: () {
onCatgoryTap(categorys[index]);
},
);
},
);
}
}
class ListCatWidget extends StatelessWidget {
final Cat cat;
final VoidCallback onTap;
ListCatWidget({
this.cat,
this.onTap
});
@override
Widget build(BuildContext context) {
return new Container(
color: Colors.green,
child: Container(
decoration: new BoxDecoration(
color: Colors.white,
border: Border(
right: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
bottom: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
),
),
child: new RaisedButton(
onPressed: () {
onTap();
},
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Icon(
Icons.add
),
Text(cat.name),
],
)
)
)
);
}
}
class ListItemWidget extends StatelessWidget {
final WidgetPoint widgetPoint;
final VoidCallback onTap;
ListItemWidget({
this.widgetPoint,
this.onTap
});
@override
Widget build(BuildContext context) {
return new Container(
color: Colors.green,
child: Container(
decoration: new BoxDecoration(
color: Colors.white,
border: Border(
right: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
bottom: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
),
),
child: new RaisedButton(
onPressed: () {
onTap();
},
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Icon(
Icons.add
),
Text(widgetPoint.name),
],
)
)
)
);
}
}

119
lib/views/widgetPage.dart Normal file
View File

@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import '../routers/application.dart';
import '../model/cat.dart';
import '../widgets/index.dart';
class WidgetPage extends StatefulWidget {
final db;
final CatControlModel catModel;
WidgetPage(this.db): catModel = new CatControlModel(),super();
@override
SecondPageState createState() => new SecondPageState(catModel);
}
class SecondPageState extends State<WidgetPage> {
CatControlModel catModel;
SecondPageState(this.catModel): super();
TextEditingController controller;
String active = 'test';
String data = '';
List<Cat> categories = [];
void initState() {
renderCats();
}
void renderCats(){
catModel.getList().then((List data){
if(data.isNotEmpty){
setState(() {
categories = data;
});
}
});
}
@override
Widget build(BuildContext context) {
if (categories.length == 0) {
return new Container();
}
print("categories in widgetPage : ${categories[0]}");
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, //每行2个
mainAxisSpacing: 0.0, //主轴(竖直)方向间距
crossAxisSpacing: 0.0, //纵轴(水平)方向间距
childAspectRatio: 0.8 //纵轴缩放比例
),
itemCount: categories.length,
itemBuilder: (BuildContext context, int index) {
return new ListItemWidget(
category: categories[index],
);
},
);
}
void _onChanged(String value) {
setState(() {
active = value;
data = '90';
});
}
}
class ListItemWidget extends StatelessWidget {
final Cat category;
ListItemWidget({this.category});
@override
Widget build(BuildContext context) {
return new Container(
color: Colors.green,
child: Container(
decoration: new BoxDecoration(
color: Colors.white,
border: Border(
right: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
bottom: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
),
),
child: new RaisedButton(
onPressed: () {
Application.router.navigateTo(context, "/category/${category.name}");
// Application.router.navigateTo(context, "/category/${category.name}");
},
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Icon(
Icons.add,
),
Text(category.name),
],
)
)
)
);
}
}

16
lib/widgets/404.dart Normal file
View File

@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
class WidgetNotFound extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("widget not found"),
),
body: Container(
child: new Text("widget not found")
)
);
}
}

View File

View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
static const String routeName = '/element/Form/Button/FlatButton';
_Demo createState() => _Demo();
}
class _Demo extends State<Demo> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FlatButton"),
),
body: Container(
child: RaisedButton(onPressed: () {}, child: Text("FlatButton"))
)
);
}
}

View File

@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
static const String routeName = '/element/Form/Button/RaisedButton';
_Demo createState() => _Demo();
}
class _Demo extends State<Demo> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FlatButton"),
),
body: Container(
child: RaisedButton(onPressed: () {}, child: Text("BUtton"))
)
);
}
}

View File

@ -0,0 +1,19 @@
import '../../../../model/widget.dart';
import "package:flutter/material.dart";
import 'FlatButton/index.dart' as FlatButton;
import 'RaisedButton/index.dart' as RaisedButton;
List<WidgetPoint> widgetPoints = [
WidgetPoint(
name: 'FlatButton',
routerName: FlatButton.Demo.routeName,
buildRouter: (BuildContext context) => FlatButton.Demo(),
),
WidgetPoint(
name: 'RaisedButton',
routerName: RaisedButton.Demo.routeName,
buildRouter: (BuildContext context) => RaisedButton.Demo(),
),
];

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
_Demo createState() => _Demo();
}
class _Demo extends State<Demo> {
Widget build(BuildContext context) {
return Container(
child: TextField()
);
}
}

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
static const String routeName = '/element/Form/Text/RichText';
_Demo createState() => _Demo();
}
class _Demo extends State<Demo> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FlatButton"),
),
body: Container(
child: Text("this is RichText")
)
);
}
}

View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
static const String routeName = '/element/Form/Text/Text';
_Demo createState() => _Demo();
}
class _Demo extends State<Demo> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FlatButton"),
),
body: Container(
child: Text("this is Text")
)
);
}
}

View File

@ -0,0 +1,19 @@
import '../../../../model/widget.dart';
import "package:flutter/material.dart";
import 'RichText/index.dart' as RichText;
import 'Text/index.dart' as Text;
List<WidgetPoint> widgetPoints = [
WidgetPoint(
name: 'RichText',
routerName: RichText.Demo.routeName,
buildRouter: (BuildContext context) => RichText.Demo(),
),
WidgetPoint(
name: 'Text',
routerName: Text.Demo.routeName,
buildRouter: (BuildContext context) => Text.Demo(),
),
];

View File

@ -0,0 +1,9 @@
import 'Button/index.dart' as Button;
import 'Text/index.dart' as Text;
List getWidgets() {
List result = [];
result.addAll(Button.widgetPoints);
result.addAll(Text.widgetPoints);
return result;
}

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1 @@
import 'package:flutter/material.dart';

View File

@ -0,0 +1,7 @@
import 'Form/index.dart' as Form;
List getWidgets() {
List result = [];
result.addAll(Form.getWidgets());
return result;
}

8
lib/widgets/index.dart Normal file
View File

@ -0,0 +1,8 @@
import 'elements/index.dart' as elements;
class WidgetDemoList {
WidgetDemoList();
List getDemos() {
return elements.getWidgets();
}
}

View File

72
pubspec.yaml Normal file
View File

@ -0,0 +1,72 @@
name: flutter_rookie_book
description: flutter_rookie_book
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# Read more about versioning at semver.org.
version: 0.0.3
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
event_bus: ^1.0.1
fluro: ^1.3.4
image_picker: ^0.4.10
sqflite: ^0.12.1
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- assets/app.db
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.io/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.io/custom-fonts/#from-packages

View File

@ -0,0 +1 @@
{}

0
test/imp.dart Normal file
View File

1
test/imp2.dart Normal file
View File

@ -0,0 +1 @@
var router = [12,13,14];

0
test/test.dart Normal file
View File

29
test/widget_test.dart Normal file
View File

@ -0,0 +1,29 @@
// This is a basic Flutter widget test.
// To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter
// provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to
// find child widgets in the widget tree, read text, and verify that the values of widget properties
// are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_rookie_book/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(new MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}