mirror of
https://github.com/alibaba/flutter-go.git
synced 2025-05-17 21:05:56 +08:00
add files
This commit is contained in:
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal 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
160
README.md
Normal 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
|
||||
```
|
BIN
assets/app.db
Normal file
BIN
assets/app.db
Normal file
Binary file not shown.
BIN
docs/17_02_18__11_13_2018.jpg
Normal file
BIN
docs/17_02_18__11_13_2018.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 360 KiB |
BIN
docs/17_47_49__11_13_2018.jpg
Normal file
BIN
docs/17_47_49__11_13_2018.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 673 KiB |
BIN
docs/20_03_01__11_13_2018.jpg
Normal file
BIN
docs/20_03_01__11_13_2018.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 215 KiB |
BIN
docs/36B53C6F-98F1-452B-B45A-A93293CC6B3C.png
Normal file
BIN
docs/36B53C6F-98F1-452B-B45A-A93293CC6B3C.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
23
lib/common/Style.dart
Normal file
23
lib/common/Style.dart
Normal 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
8
lib/common/eventBus.dart
Normal 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
32
lib/common/provider.dart
Normal 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
66
lib/common/sql.dart
Normal 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
26
lib/components/Input.dart
Normal 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
100
lib/components/List.dart
Normal 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,
|
||||
),
|
||||
) ;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
}
|
68
lib/components/Pagination.dart
Normal file
68
lib/components/Pagination.dart
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
112
lib/components/Pagination2.dart
Normal file
112
lib/components/Pagination2.dart
Normal 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),
|
||||
);
|
||||
}
|
||||
}
|
372
lib/components/SearchInput.dart
Normal file
372
lib/components/SearchInput.dart
Normal 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
|
140
lib/components/homeBanner.dart
Normal file
140
lib/components/homeBanner.dart
Normal 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
78
lib/generated/i18n.dart
Normal 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
150
lib/main.dart
Normal 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
11
lib/model/base.dart
Normal 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
108
lib/model/cat.dart
Normal 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
21
lib/model/story.dart
Normal 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
112
lib/model/widget.dart
Normal 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);
|
||||
}
|
||||
}
|
5
lib/routers/application.dart
Normal file
5
lib/routers/application.dart
Normal file
@ -0,0 +1,5 @@
|
||||
import 'package:fluro/fluro.dart';
|
||||
|
||||
class Application {
|
||||
static Router router;
|
||||
}
|
20
lib/routers/router_handler.dart
Normal file
20
lib/routers/router_handler.dart
Normal 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
30
lib/routers/routers.dart
Normal 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
22
lib/views/Detail.dart
Normal 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
38
lib/views/FirstPage.dart
Normal 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
22
lib/views/FourthPage.dart
Normal 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
69
lib/views/ThirdPage.dart
Normal 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
251
lib/views/category.dart
Normal 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
119
lib/views/widgetPage.dart
Normal 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
16
lib/widgets/404.dart
Normal 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")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
0
lib/widgets/components/index.dart
Normal file
0
lib/widgets/components/index.dart
Normal file
20
lib/widgets/elements/Form/Button/FlatButton/index.dart
Normal file
20
lib/widgets/elements/Form/Button/FlatButton/index.dart
Normal 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"))
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
21
lib/widgets/elements/Form/Button/RaisedButton/index.dart
Normal file
21
lib/widgets/elements/Form/Button/RaisedButton/index.dart
Normal 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"))
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
19
lib/widgets/elements/Form/Button/index.dart
Normal file
19
lib/widgets/elements/Form/Button/index.dart
Normal 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(),
|
||||
),
|
||||
];
|
1
lib/widgets/elements/Form/CheckBox/index.dart
Normal file
1
lib/widgets/elements/Form/CheckBox/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
14
lib/widgets/elements/Form/Input/TextField/index.dart
Normal file
14
lib/widgets/elements/Form/Input/TextField/index.dart
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
1
lib/widgets/elements/Form/Input/index.dart
Normal file
1
lib/widgets/elements/Form/Input/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Form/Radio/index.dart
Normal file
1
lib/widgets/elements/Form/Radio/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Form/Slider/index.dart
Normal file
1
lib/widgets/elements/Form/Slider/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Form/Switch/index.dart
Normal file
1
lib/widgets/elements/Form/Switch/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
20
lib/widgets/elements/Form/Text/RichText/index.dart
Normal file
20
lib/widgets/elements/Form/Text/RichText/index.dart
Normal 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")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
20
lib/widgets/elements/Form/Text/Text/index.dart
Normal file
20
lib/widgets/elements/Form/Text/Text/index.dart
Normal 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")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
19
lib/widgets/elements/Form/Text/index.dart
Normal file
19
lib/widgets/elements/Form/Text/index.dart
Normal 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(),
|
||||
),
|
||||
];
|
9
lib/widgets/elements/Form/index.dart
Normal file
9
lib/widgets/elements/Form/index.dart
Normal 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;
|
||||
}
|
1
lib/widgets/elements/Frame/Align/index.dart
Normal file
1
lib/widgets/elements/Frame/Align/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Frame/Axis/index.dart
Normal file
1
lib/widgets/elements/Frame/Axis/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Frame/Box/index.dart
Normal file
1
lib/widgets/elements/Frame/Box/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Frame/Expanded/index.dart
Normal file
1
lib/widgets/elements/Frame/Expanded/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Frame/Layout/index.dart
Normal file
1
lib/widgets/elements/Frame/Layout/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Frame/Stack/index.dart
Normal file
1
lib/widgets/elements/Frame/Stack/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Frame/Table/index.dart
Normal file
1
lib/widgets/elements/Frame/Table/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Frame/spacing/index.dart
Normal file
1
lib/widgets/elements/Frame/spacing/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Media/Canvas/index.dart
Normal file
1
lib/widgets/elements/Media/Canvas/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Media/Icon/index.dart
Normal file
1
lib/widgets/elements/Media/Icon/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
1
lib/widgets/elements/Media/Image/index.dart
Normal file
1
lib/widgets/elements/Media/Image/index.dart
Normal file
@ -0,0 +1 @@
|
||||
import 'package:flutter/material.dart';
|
7
lib/widgets/elements/index.dart
Normal file
7
lib/widgets/elements/index.dart
Normal 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
8
lib/widgets/index.dart
Normal file
@ -0,0 +1,8 @@
|
||||
import 'elements/index.dart' as elements;
|
||||
|
||||
class WidgetDemoList {
|
||||
WidgetDemoList();
|
||||
List getDemos() {
|
||||
return elements.getWidgets();
|
||||
}
|
||||
}
|
0
lib/widgets/themes/index.dart
Normal file
0
lib/widgets/themes/index.dart
Normal file
72
pubspec.yaml
Normal file
72
pubspec.yaml
Normal 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
|
1
res/values/strings_en.arb
Normal file
1
res/values/strings_en.arb
Normal file
@ -0,0 +1 @@
|
||||
{}
|
0
test/imp.dart
Normal file
0
test/imp.dart
Normal file
1
test/imp2.dart
Normal file
1
test/imp2.dart
Normal file
@ -0,0 +1 @@
|
||||
var router = [12,13,14];
|
0
test/test.dart
Normal file
0
test/test.dart
Normal file
29
test/widget_test.dart
Normal file
29
test/widget_test.dart
Normal 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);
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user