mirror of
https://github.com/alibaba/flutter-go.git
synced 2025-07-02 21:48:54 +08:00
Add:创建 flutter go web 版
This commit is contained in:
75
README copy.md
Normal file
75
README copy.md
Normal file
@ -0,0 +1,75 @@
|
||||
安装flutter_web构建工具
|
||||
要安装webdev包为Flutter for web提供构建工具的 包,请运行以下命令:
|
||||
|
||||
$ flutter packages pub global activate webdev
|
||||
确保$HOME/.pub-cache/bin目录 在您的路径中,然后您可以webdev直接从终端使用该命令。
|
||||
|
||||
注意:如果您在配置webdev直接运行时遇到问题,请尝试:
|
||||
flutter packages pub global run webdev [command]。
|
||||
|
||||
运行hello_world示例
|
||||
该示例存在于examples/hello_world存储库中。
|
||||
|
||||
$ cd examples / hello_world /
|
||||
更新包。
|
||||
|
||||
$ flutter包升级
|
||||
!flutter_web 0.0.0 from path ../../flutter_web
|
||||
!flutter_web_ui 0.0.0 from path ../../flutter_web_ui
|
||||
在hello_world中运行“flutter packages upgrade”... 5.0s
|
||||
如果成功,你就可以运行了!
|
||||
|
||||
在本地构建并提供示例。
|
||||
|
||||
$ webdev serve
|
||||
[INFO]生成构建脚本完成,耗时331ms
|
||||
...
|
||||
[INFO]建立新资产图完成,耗时1.4s
|
||||
...
|
||||
[INFO]运行构建完成,耗时27.9s
|
||||
...
|
||||
[INFO] 28.1后成功s有618个输出(3233个动作)
|
||||
在http:// localhost:8080上提供`web`
|
||||
在Chrome中打开http:// localhost:8080,您应该会Hello World在左上角看到红色文字。
|
||||
|
||||
获取(无状态)热重载 webdev
|
||||
要webdev与热重载一起使用,请在项目目录中运行以下命令:
|
||||
|
||||
$ webdev serve --auto restart
|
||||
您会注意到````的相似输出,flutter packages pub run build_runner serve 但现在更改为您的应用程序代码应该导致在保存时快速刷新应用程序。
|
||||
|
||||
注意:该--hot-reload选项并不完美。如果发现意外行为,则可能需要手动刷新页面。
|
||||
|
||||
注意:该--hot-reload选项目前是“无状态”。重新加载时应用程序状态将丢失。我们希望在网上提供“有状态”的热重载 - 我们正积极致力于此!
|
||||
|
||||
使用生产JavaScript编译器构建
|
||||
上面介绍的工作流程(可用于build_runner和webdev)使用Dart Dev Compiler,它专为快速,增量编译和简单调试而设计。
|
||||
|
||||
如果您想评估生产性能,浏览器兼容性和代码大小,可以启用我们的发布编译器 dart2js。
|
||||
|
||||
要启用发布编译器,请传入--release标志(或只是-r)。
|
||||
|
||||
$ webdev serve -r
|
||||
注意:此配置中的构建可能会更慢。
|
||||
|
||||
如果您想生成输出到磁盘,我们建议您使用webdev。
|
||||
|
||||
$ webdev build
|
||||
这将创建一个build目录index.html,main.dart.js以及使用静态HTTP服务器运行应用程序所需的其余文件。
|
||||
|
||||
要优化输出JavaScript,可以使用build.yaml项目根目录中的文件启用优化标志 ,其中包含以下内容:
|
||||
|
||||
#请参阅https://github.com/dart-lang/build/tree/master/build_web_compilers#configuration
|
||||
目标:
|
||||
$ default:
|
||||
builders:
|
||||
build_web_compilers | entrypoint:
|
||||
generate_for:
|
||||
- web / **。dart
|
||||
选项:
|
||||
dart2js_args:
|
||||
- --no-source-maps
|
||||
- -O4
|
||||
注意:该-O4选项启用了许多高级优化,这些优化可能会导致未经过全面测试的代码中的运行时错误。
|
||||
|
||||
uglifyjs project/ali-flutter-go/build/main.dart.js -o project/ali-flutter-go/build/main.dart.js
|
121
README.md
121
README.md
@ -1,75 +1,80 @@
|
||||
安装flutter_web构建工具
|
||||
要安装webdev包为Flutter for web提供构建工具的 包,请运行以下命令:
|
||||
### 欢迎来到 `Flutter GO for web` 的代码库。
|
||||
##### 此代码库是配合 [Flutter-web](https://github.com/flutter/flutter_web) 官方预览版, 而对 `Flutter Go` 项目做的 `web` 版本的移植。
|
||||
|
||||
$ flutter packages pub global activate webdev
|
||||
确保$HOME/.pub-cache/bin目录 在您的路径中,然后您可以webdev直接从终端使用该命令。
|
||||
#### `Flutter的Web`支持尚不稳定。我们将此版本指定为技术预览版; 对 `Flutter Go native` 版本移植还原度大概在 80%左右。旨在帮助 `Flutter` 开发人员,快速熟悉 `Flutter-web` 官方的实践。
|
||||
|
||||
注意:如果您在配置webdev直接运行时遇到问题,请尝试:
|
||||
flutter packages pub global run webdev [command]。
|
||||
#### [在 `Flutter` 官方宣布Flutter暂时不会开发热更新(Code push)](https://github.com/flutter/flutter/issues/14330) 之后, `Flutter-web` 无疑是的动态更新代码最快捷的方式,虽然不是最佳方案,但是是最易用降级方案。
|
||||
|
||||
运行hello_world示例
|
||||
该示例存在于examples/hello_world存储库中。
|
||||
#### 通过 `Flutter-go-web` 版本的学习,可以快速,有效还原 `Flutter-go` native 版本。帮助开发者,对快速版本H5化,做一个样例展示,提供 `Flutter-web` 的能力演示。
|
||||
|
||||
$ cd examples / hello_world /
|
||||
更新包。
|
||||
### 使用前准备
|
||||
- [请了解 `Flutter-web` 官方说明](https://github.com/flutter/flutter_web/blob/master/README.md)
|
||||
|
||||
$ flutter包升级
|
||||
!flutter_web 0.0.0 from path ../../flutter_web
|
||||
!flutter_web_ui 0.0.0 from path ../../flutter_web_ui
|
||||
在hello_world中运行“flutter packages upgrade”... 5.0s
|
||||
如果成功,你就可以运行了!
|
||||
### 如何使用
|
||||
- git 拉取 `Flutter-go` 项目,并切换到 `web/flutter-go-web-0.0.1` 分支
|
||||
```dart
|
||||
$ git clone https://github.com/alibaba/flutter-go.git flutter-go
|
||||
|
||||
在本地构建并提供示例。
|
||||
$ git checkout web/flutter-go-web-0.0.1
|
||||
```
|
||||
|
||||
$ webdev serve
|
||||
[INFO]生成构建脚本完成,耗时331ms
|
||||
...
|
||||
[INFO]建立新资产图完成,耗时1.4s
|
||||
...
|
||||
[INFO]运行构建完成,耗时27.9s
|
||||
...
|
||||
[INFO] 28.1后成功s有618个输出(3233个动作)
|
||||
在http:// localhost:8080上提供`web`
|
||||
在Chrome中打开http:// localhost:8080,您应该会Hello World在左上角看到红色文字。
|
||||
- 安装flutter_web构建工具
|
||||
```dart
|
||||
$ flutter pub global activate webdev
|
||||
```
|
||||
|
||||
获取(无状态)热重载 webdev
|
||||
要webdev与热重载一起使用,请在项目目录中运行以下命令:
|
||||
- 更新pub包
|
||||
```dart
|
||||
$ pub get
|
||||
|
||||
$ webdev serve --auto restart
|
||||
您会注意到````的相似输出,flutter packages pub run build_runner serve 但现在更改为您的应用程序代码应该导致在保存时快速刷新应用程序。
|
||||
//...
|
||||
Resolving dependencies...
|
||||
Warning: You are using these overridden dependencies:
|
||||
! flutter_web 0.0.0 from git https://github.com/flutter/flutter_web at 6cabfc in packages/flutter_web
|
||||
! flutter_web_test 0.0.0 from git https://github.com/flutter/flutter_web at 6cabfc in packages/flutter_web_test
|
||||
! flutter_web_ui 0.0.0 from git https://github.com/flutter/flutter_web at 6cabfc in packages/flutter_web_ui
|
||||
Got dependencies!
|
||||
Precompiling executables... (12.0s)
|
||||
```
|
||||
|
||||
注意:该--hot-reload选项并不完美。如果发现意外行为,则可能需要手动刷新页面。
|
||||
- 开发模式,获取(无状态)热重载 webdev
|
||||
```dart
|
||||
$ webdev serve --auto restart
|
||||
|
||||
注意:该--hot-reload选项目前是“无状态”。重新加载时应用程序状态将丢失。我们希望在网上提供“有状态”的热重载 - 我们正积极致力于此!
|
||||
[INFO] Building new asset graph completed, took 2.0s
|
||||
[INFO] Checking for unexpected pre-existing outputs. completed, took 1ms
|
||||
[INFO] Serving `web` on http://127.0.0.1:8080
|
||||
[INFO] Running build completed, took 49.7s
|
||||
[INFO] Caching finalized dependency graph completed, took 421ms
|
||||
[INFO] Succeeded after 50.1s with 3309 outputs (9338 actions)
|
||||
```
|
||||
|
||||
使用生产JavaScript编译器构建
|
||||
上面介绍的工作流程(可用于build_runner和webdev)使用Dart Dev Compiler,它专为快速,增量编译和简单调试而设计。
|
||||
- 发布模式,创建最终编译结果,这将创建一个build目录`index.html`,`main.dart.js`以及使用静态HTTP服务器运行应用程序所需的其余文件。
|
||||
```dart
|
||||
$ webdev build
|
||||
```
|
||||
|
||||
如果您想评估生产性能,浏览器兼容性和代码大小,可以启用我们的发布编译器 dart2js。
|
||||
pub global activate --source path /Users/ryan/work/ali/github/flutter-project/@ali-flutter-go/tools/
|
||||
|
||||
要启用发布编译器,请传入--release标志(或只是-r)。
|
||||
### 通过上面操作基本就能运行,`flutter-go-web` 的界面
|
||||
|
||||
$ webdev serve -r
|
||||
注意:此配置中的构建可能会更慢。
|
||||
### 为了对已有 `native` 工程快速生成 `flutter-web` 版本,可以使用 `trans2fw` 工具
|
||||
- 首先拷贝 native 版本下的lib 文件夹到当前目录, 并重命名如: lib-cp。
|
||||
|
||||
如果您想生成输出到磁盘,我们建议您使用webdev。
|
||||
- 安装 `trans2fw` 工具, 并注册程序
|
||||
```dart
|
||||
$ pub global activate trans2fw
|
||||
$ pub global run trans2fw
|
||||
```
|
||||
|
||||
$ webdev build
|
||||
这将创建一个build目录index.html,main.dart.js以及使用静态HTTP服务器运行应用程序所需的其余文件。
|
||||
|
||||
要优化输出JavaScript,可以使用build.yaml项目根目录中的文件启用优化标志 ,其中包含以下内容:
|
||||
|
||||
#请参阅https://github.com/dart-lang/build/tree/master/build_web_compilers#configuration
|
||||
目标:
|
||||
$ default:
|
||||
builders:
|
||||
build_web_compilers | entrypoint:
|
||||
generate_for:
|
||||
- web / **。dart
|
||||
选项:
|
||||
dart2js_args:
|
||||
- --no-source-maps
|
||||
- -O4
|
||||
注意:该-O4选项启用了许多高级优化,这些优化可能会导致未经过全面测试的代码中的运行时错误。
|
||||
|
||||
uglifyjs project/ali-flutter-go/build/main.dart.js -o project/ali-flutter-go/build/main.dart.js
|
||||
- 运行 `trans2fw` 工具,根据提示输入
|
||||
```dart
|
||||
$ trans2fw
|
||||
[✓] 请输入需要转换的文件目录(相对于项目目录): lib-cp
|
||||
[✓] 是否覆盖原有文件 ? (y/N) y
|
||||
{path: lib-cp, cover: true}
|
||||
使用输入文件目录:lib-cp
|
||||
被转换的文件::lib-cp/Pagination.dart::32
|
||||
被转换的文件::lib-cp/homeBanner-copy-copy.dart::32
|
||||
...
|
||||
```
|
100
lib-cp/CompList-copy-copy-copy.dart
Normal file
100
lib-cp/CompList-copy-copy-copy.dart
Normal file
@ -0,0 +1,100 @@
|
||||
import 'package:flutter_web/material.dart';
|
||||
import 'package:flutter_rookie_book/views/Detail.dart';
|
||||
|
||||
class CompList extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
// TODO: implement createState
|
||||
return new ListState();
|
||||
}
|
||||
}
|
||||
|
||||
class ListState extends State<CompList> {
|
||||
@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,
|
||||
),
|
||||
) ;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
}
|
100
lib-cp/CompList-copy-copy.dart
Normal file
100
lib-cp/CompList-copy-copy.dart
Normal file
@ -0,0 +1,100 @@
|
||||
import 'package:flutter_web/material.dart';
|
||||
import 'package:flutter_rookie_book/views/Detail.dart';
|
||||
|
||||
class CompList extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
// TODO: implement createState
|
||||
return new ListState();
|
||||
}
|
||||
}
|
||||
|
||||
class ListState extends State<CompList> {
|
||||
@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,
|
||||
),
|
||||
) ;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
}
|
100
lib-cp/CompList-copy.dart
Normal file
100
lib-cp/CompList-copy.dart
Normal file
@ -0,0 +1,100 @@
|
||||
import 'package:flutter_web/material.dart';
|
||||
import 'package:flutter_rookie_book/views/Detail.dart';
|
||||
|
||||
class CompList extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
// TODO: implement createState
|
||||
return new ListState();
|
||||
}
|
||||
}
|
||||
|
||||
class ListState extends State<CompList> {
|
||||
@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,
|
||||
),
|
||||
) ;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
}
|
100
lib-cp/CompList.dart
Normal file
100
lib-cp/CompList.dart
Normal file
@ -0,0 +1,100 @@
|
||||
import 'package:flutter_web/material.dart';
|
||||
import 'package:flutter_rookie_book/views/Detail.dart';
|
||||
|
||||
class CompList extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
// TODO: implement createState
|
||||
return new ListState();
|
||||
}
|
||||
}
|
||||
|
||||
class ListState extends State<CompList> {
|
||||
@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,
|
||||
),
|
||||
) ;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
}
|
178
lib-cp/ListRefresh-copy-copy-copy.dart
Normal file
178
lib-cp/ListRefresh-copy-copy-copy.dart
Normal file
@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Created with Android Studio.
|
||||
* User: 一晟
|
||||
* Date: 2019/1/4
|
||||
* Time: 上午1:16
|
||||
* email: zhu.yan@alibaba-inc.com
|
||||
* tartget: FlatButton 的示例
|
||||
*/
|
||||
import 'package:flutter_web/material.dart';
|
||||
import 'dart:math';
|
||||
|
||||
|
||||
class ListRefresh extends StatefulWidget {
|
||||
final renderItem;
|
||||
final requestApi;
|
||||
|
||||
const ListRefresh([this.requestApi,this.renderItem]) : super();
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => listRefresh();
|
||||
}
|
||||
|
||||
class listRefresh extends State<ListRefresh> {
|
||||
bool isLoading = false; // 是否正在请求数据中
|
||||
bool _hasMore = true; // 是否还有更多数据可加载
|
||||
int _pageIndex = 0; // 页面的索引
|
||||
int _pageTotal = 0; // 页面的索引
|
||||
List items = new List();
|
||||
ScrollController _scrollController = new ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getMoreData();
|
||||
_scrollController.addListener(() {
|
||||
// 如果下拉的当前位置到scroll的最下面
|
||||
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
|
||||
_getMoreData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 回弹效果
|
||||
* */
|
||||
backElasticEffect(){
|
||||
// double edge = 50.0;
|
||||
// double offsetFromBottom = _scrollController.position.maxScrollExtent - _scrollController.position.pixels;
|
||||
// if (offsetFromBottom < edge) { // 添加一个动画没有更多数据的时候 ListView 向下移动覆盖正在加载更多数据的标志
|
||||
// _scrollController.animateTo(
|
||||
// _scrollController.offset - (edge -offsetFromBottom),
|
||||
// duration: new Duration(milliseconds: 1000),
|
||||
// curve: Curves.easeOut);
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
* list探底,执行的具体事件
|
||||
* */
|
||||
Future _getMoreData() async {
|
||||
if (!isLoading && _hasMore) { // 如果上一次异步请求数据完成 同时有数据可以加载
|
||||
setState(() => isLoading = true);
|
||||
//if(_hasMore){ // 还有数据可以拉新
|
||||
List newEntries = await mokeHttpRequest();
|
||||
//if (newEntries.isEmpty) {
|
||||
_hasMore = (_pageIndex <= _pageTotal);
|
||||
setState(() {
|
||||
items.addAll(newEntries);
|
||||
isLoading = false;
|
||||
});
|
||||
backElasticEffect();
|
||||
}else if (!isLoading && !_hasMore){ // 这样判断,减少以后的绘制
|
||||
_pageIndex = 0;
|
||||
backElasticEffect();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 伪装吐出新数据
|
||||
* */
|
||||
Future<List> mokeHttpRequest() async {
|
||||
if(widget.requestApi is Function){
|
||||
final listObj = await widget.requestApi({'pageIndex':_pageIndex});
|
||||
_pageIndex = listObj['pageIndex'];
|
||||
_pageTotal = listObj['total'];
|
||||
return listObj['list'];
|
||||
}else {
|
||||
return Future.delayed(Duration(seconds: 2), () {
|
||||
return [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 下拉加载的事件,清空之前list内容,取前X个
|
||||
* 其实就是列表重置
|
||||
* */
|
||||
Future<Null> _handleRefresh() async {
|
||||
List newEntries = await mokeHttpRequest();
|
||||
setState(() {
|
||||
items.clear();
|
||||
items.addAll(newEntries);
|
||||
isLoading = false;
|
||||
_hasMore = true;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 加载中的提示
|
||||
* */
|
||||
Widget _buildLoadText() {
|
||||
return Container(child: Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: Center(
|
||||
child: Text("数据没有更多了!!!"),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* 上提加载loading的widget,如果数据到达极限,显示没有更多
|
||||
* */
|
||||
Widget _buildProgressIndicator() {
|
||||
if(_hasMore){
|
||||
return new Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: new Center(
|
||||
child:Column(
|
||||
children: <Widget>[
|
||||
new Opacity(
|
||||
opacity: isLoading ? 1.0 : 0.0,
|
||||
child: new CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.blue)),
|
||||
),
|
||||
SizedBox(height:20.0),
|
||||
Text('稍等片刻更精彩...',style: TextStyle(fontSize: 14.0),)
|
||||
],)
|
||||
//child:
|
||||
),
|
||||
);
|
||||
}else {
|
||||
return _buildLoadText();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_scrollController.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new RefreshIndicator(
|
||||
child:ListView.builder(
|
||||
itemCount: items.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if(index == 0 && index != items.length){
|
||||
return Container(height:0);
|
||||
}
|
||||
if (index == items.length) {
|
||||
//return _buildLoadText();
|
||||
return _buildProgressIndicator();
|
||||
} else {
|
||||
//print('itemsitemsitemsitems:${items[index].title}');
|
||||
//return ListTile(title: Text("Index${index}:${items[index].title}"));
|
||||
if (widget.renderItem is Function) {
|
||||
return widget.renderItem(index,items[index]);
|
||||
}
|
||||
//return makeCard(index,items[index]);
|
||||
}
|
||||
},
|
||||
controller: _scrollController,
|
||||
),
|
||||
onRefresh: _handleRefresh,
|
||||
);
|
||||
}
|
||||
}
|
178
lib-cp/ListRefresh-copy-copy.dart
Normal file
178
lib-cp/ListRefresh-copy-copy.dart
Normal file
@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Created with Android Studio.
|
||||
* User: 一晟
|
||||
* Date: 2019/1/4
|
||||
* Time: 上午1:16
|
||||
* email: zhu.yan@alibaba-inc.com
|
||||
* tartget: FlatButton 的示例
|
||||
*/
|
||||
import 'package:flutter_web/material.dart';
|
||||
import 'dart:math';
|
||||
|
||||
|
||||
class ListRefresh extends StatefulWidget {
|
||||
final renderItem;
|
||||
final requestApi;
|
||||
|
||||
const ListRefresh([this.requestApi,this.renderItem]) : super();
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => listRefresh();
|
||||
}
|
||||
|
||||
class listRefresh extends State<ListRefresh> {
|
||||
bool isLoading = false; // 是否正在请求数据中
|
||||
bool _hasMore = true; // 是否还有更多数据可加载
|
||||
int _pageIndex = 0; // 页面的索引
|
||||
int _pageTotal = 0; // 页面的索引
|
||||
List items = new List();
|
||||
ScrollController _scrollController = new ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getMoreData();
|
||||
_scrollController.addListener(() {
|
||||
// 如果下拉的当前位置到scroll的最下面
|
||||
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
|
||||
_getMoreData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 回弹效果
|
||||
* */
|
||||
backElasticEffect(){
|
||||
// double edge = 50.0;
|
||||
// double offsetFromBottom = _scrollController.position.maxScrollExtent - _scrollController.position.pixels;
|
||||
// if (offsetFromBottom < edge) { // 添加一个动画没有更多数据的时候 ListView 向下移动覆盖正在加载更多数据的标志
|
||||
// _scrollController.animateTo(
|
||||
// _scrollController.offset - (edge -offsetFromBottom),
|
||||
// duration: new Duration(milliseconds: 1000),
|
||||
// curve: Curves.easeOut);
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
* list探底,执行的具体事件
|
||||
* */
|
||||
Future _getMoreData() async {
|
||||
if (!isLoading && _hasMore) { // 如果上一次异步请求数据完成 同时有数据可以加载
|
||||
setState(() => isLoading = true);
|
||||
//if(_hasMore){ // 还有数据可以拉新
|
||||
List newEntries = await mokeHttpRequest();
|
||||
//if (newEntries.isEmpty) {
|
||||
_hasMore = (_pageIndex <= _pageTotal);
|
||||
setState(() {
|
||||
items.addAll(newEntries);
|
||||
isLoading = false;
|
||||
});
|
||||
backElasticEffect();
|
||||
}else if (!isLoading && !_hasMore){ // 这样判断,减少以后的绘制
|
||||
_pageIndex = 0;
|
||||
backElasticEffect();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 伪装吐出新数据
|
||||
* */
|
||||
Future<List> mokeHttpRequest() async {
|
||||
if(widget.requestApi is Function){
|
||||
final listObj = await widget.requestApi({'pageIndex':_pageIndex});
|
||||
_pageIndex = listObj['pageIndex'];
|
||||
_pageTotal = listObj['total'];
|
||||
return listObj['list'];
|
||||
}else {
|
||||
return Future.delayed(Duration(seconds: 2), () {
|
||||
return [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 下拉加载的事件,清空之前list内容,取前X个
|
||||
* 其实就是列表重置
|
||||
* */
|
||||
Future<Null> _handleRefresh() async {
|
||||
List newEntries = await mokeHttpRequest();
|
||||
setState(() {
|
||||
items.clear();
|
||||
items.addAll(newEntries);
|
||||
isLoading = false;
|
||||
_hasMore = true;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 加载中的提示
|
||||
* */
|
||||
Widget _buildLoadText() {
|
||||
return Container(child: Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: Center(
|
||||
child: Text("数据没有更多了!!!"),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* 上提加载loading的widget,如果数据到达极限,显示没有更多
|
||||
* */
|
||||
Widget _buildProgressIndicator() {
|
||||
if(_hasMore){
|
||||
return new Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: new Center(
|
||||
child:Column(
|
||||
children: <Widget>[
|
||||
new Opacity(
|
||||
opacity: isLoading ? 1.0 : 0.0,
|
||||
child: new CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.blue)),
|
||||
),
|
||||
SizedBox(height:20.0),
|
||||
Text('稍等片刻更精彩...',style: TextStyle(fontSize: 14.0),)
|
||||
],)
|
||||
//child:
|
||||
),
|
||||
);
|
||||
}else {
|
||||
return _buildLoadText();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_scrollController.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new RefreshIndicator(
|
||||
child:ListView.builder(
|
||||
itemCount: items.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if(index == 0 && index != items.length){
|
||||
return Container(height:0);
|
||||
}
|
||||
if (index == items.length) {
|
||||
//return _buildLoadText();
|
||||
return _buildProgressIndicator();
|
||||
} else {
|
||||
//print('itemsitemsitemsitems:${items[index].title}');
|
||||
//return ListTile(title: Text("Index${index}:${items[index].title}"));
|
||||
if (widget.renderItem is Function) {
|
||||
return widget.renderItem(index,items[index]);
|
||||
}
|
||||
//return makeCard(index,items[index]);
|
||||
}
|
||||
},
|
||||
controller: _scrollController,
|
||||
),
|
||||
onRefresh: _handleRefresh,
|
||||
);
|
||||
}
|
||||
}
|
178
lib-cp/ListRefresh-copy.dart
Normal file
178
lib-cp/ListRefresh-copy.dart
Normal file
@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Created with Android Studio.
|
||||
* User: 一晟
|
||||
* Date: 2019/1/4
|
||||
* Time: 上午1:16
|
||||
* email: zhu.yan@alibaba-inc.com
|
||||
* tartget: FlatButton 的示例
|
||||
*/
|
||||
import 'package:flutter_web/material.dart';
|
||||
import 'dart:math';
|
||||
|
||||
|
||||
class ListRefresh extends StatefulWidget {
|
||||
final renderItem;
|
||||
final requestApi;
|
||||
|
||||
const ListRefresh([this.requestApi,this.renderItem]) : super();
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => listRefresh();
|
||||
}
|
||||
|
||||
class listRefresh extends State<ListRefresh> {
|
||||
bool isLoading = false; // 是否正在请求数据中
|
||||
bool _hasMore = true; // 是否还有更多数据可加载
|
||||
int _pageIndex = 0; // 页面的索引
|
||||
int _pageTotal = 0; // 页面的索引
|
||||
List items = new List();
|
||||
ScrollController _scrollController = new ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getMoreData();
|
||||
_scrollController.addListener(() {
|
||||
// 如果下拉的当前位置到scroll的最下面
|
||||
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
|
||||
_getMoreData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 回弹效果
|
||||
* */
|
||||
backElasticEffect(){
|
||||
// double edge = 50.0;
|
||||
// double offsetFromBottom = _scrollController.position.maxScrollExtent - _scrollController.position.pixels;
|
||||
// if (offsetFromBottom < edge) { // 添加一个动画没有更多数据的时候 ListView 向下移动覆盖正在加载更多数据的标志
|
||||
// _scrollController.animateTo(
|
||||
// _scrollController.offset - (edge -offsetFromBottom),
|
||||
// duration: new Duration(milliseconds: 1000),
|
||||
// curve: Curves.easeOut);
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
* list探底,执行的具体事件
|
||||
* */
|
||||
Future _getMoreData() async {
|
||||
if (!isLoading && _hasMore) { // 如果上一次异步请求数据完成 同时有数据可以加载
|
||||
setState(() => isLoading = true);
|
||||
//if(_hasMore){ // 还有数据可以拉新
|
||||
List newEntries = await mokeHttpRequest();
|
||||
//if (newEntries.isEmpty) {
|
||||
_hasMore = (_pageIndex <= _pageTotal);
|
||||
setState(() {
|
||||
items.addAll(newEntries);
|
||||
isLoading = false;
|
||||
});
|
||||
backElasticEffect();
|
||||
}else if (!isLoading && !_hasMore){ // 这样判断,减少以后的绘制
|
||||
_pageIndex = 0;
|
||||
backElasticEffect();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 伪装吐出新数据
|
||||
* */
|
||||
Future<List> mokeHttpRequest() async {
|
||||
if(widget.requestApi is Function){
|
||||
final listObj = await widget.requestApi({'pageIndex':_pageIndex});
|
||||
_pageIndex = listObj['pageIndex'];
|
||||
_pageTotal = listObj['total'];
|
||||
return listObj['list'];
|
||||
}else {
|
||||
return Future.delayed(Duration(seconds: 2), () {
|
||||
return [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 下拉加载的事件,清空之前list内容,取前X个
|
||||
* 其实就是列表重置
|
||||
* */
|
||||
Future<Null> _handleRefresh() async {
|
||||
List newEntries = await mokeHttpRequest();
|
||||
setState(() {
|
||||
items.clear();
|
||||
items.addAll(newEntries);
|
||||
isLoading = false;
|
||||
_hasMore = true;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 加载中的提示
|
||||
* */
|
||||
Widget _buildLoadText() {
|
||||
return Container(child: Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: Center(
|
||||
child: Text("数据没有更多了!!!"),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* 上提加载loading的widget,如果数据到达极限,显示没有更多
|
||||
* */
|
||||
Widget _buildProgressIndicator() {
|
||||
if(_hasMore){
|
||||
return new Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: new Center(
|
||||
child:Column(
|
||||
children: <Widget>[
|
||||
new Opacity(
|
||||
opacity: isLoading ? 1.0 : 0.0,
|
||||
child: new CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.blue)),
|
||||
),
|
||||
SizedBox(height:20.0),
|
||||
Text('稍等片刻更精彩...',style: TextStyle(fontSize: 14.0),)
|
||||
],)
|
||||
//child:
|
||||
),
|
||||
);
|
||||
}else {
|
||||
return _buildLoadText();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_scrollController.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new RefreshIndicator(
|
||||
child:ListView.builder(
|
||||
itemCount: items.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if(index == 0 && index != items.length){
|
||||
return Container(height:0);
|
||||
}
|
||||
if (index == items.length) {
|
||||
//return _buildLoadText();
|
||||
return _buildProgressIndicator();
|
||||
} else {
|
||||
//print('itemsitemsitemsitems:${items[index].title}');
|
||||
//return ListTile(title: Text("Index${index}:${items[index].title}"));
|
||||
if (widget.renderItem is Function) {
|
||||
return widget.renderItem(index,items[index]);
|
||||
}
|
||||
//return makeCard(index,items[index]);
|
||||
}
|
||||
},
|
||||
controller: _scrollController,
|
||||
),
|
||||
onRefresh: _handleRefresh,
|
||||
);
|
||||
}
|
||||
}
|
178
lib-cp/ListRefresh.dart
Normal file
178
lib-cp/ListRefresh.dart
Normal file
@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Created with Android Studio.
|
||||
* User: 一晟
|
||||
* Date: 2019/1/4
|
||||
* Time: 上午1:16
|
||||
* email: zhu.yan@alibaba-inc.com
|
||||
* tartget: FlatButton 的示例
|
||||
*/
|
||||
import 'package:flutter_web/material.dart';
|
||||
import 'dart:math';
|
||||
|
||||
|
||||
class ListRefresh extends StatefulWidget {
|
||||
final renderItem;
|
||||
final requestApi;
|
||||
|
||||
const ListRefresh([this.requestApi,this.renderItem]) : super();
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => listRefresh();
|
||||
}
|
||||
|
||||
class listRefresh extends State<ListRefresh> {
|
||||
bool isLoading = false; // 是否正在请求数据中
|
||||
bool _hasMore = true; // 是否还有更多数据可加载
|
||||
int _pageIndex = 0; // 页面的索引
|
||||
int _pageTotal = 0; // 页面的索引
|
||||
List items = new List();
|
||||
ScrollController _scrollController = new ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getMoreData();
|
||||
_scrollController.addListener(() {
|
||||
// 如果下拉的当前位置到scroll的最下面
|
||||
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
|
||||
_getMoreData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 回弹效果
|
||||
* */
|
||||
backElasticEffect(){
|
||||
// double edge = 50.0;
|
||||
// double offsetFromBottom = _scrollController.position.maxScrollExtent - _scrollController.position.pixels;
|
||||
// if (offsetFromBottom < edge) { // 添加一个动画没有更多数据的时候 ListView 向下移动覆盖正在加载更多数据的标志
|
||||
// _scrollController.animateTo(
|
||||
// _scrollController.offset - (edge -offsetFromBottom),
|
||||
// duration: new Duration(milliseconds: 1000),
|
||||
// curve: Curves.easeOut);
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
* list探底,执行的具体事件
|
||||
* */
|
||||
Future _getMoreData() async {
|
||||
if (!isLoading && _hasMore) { // 如果上一次异步请求数据完成 同时有数据可以加载
|
||||
setState(() => isLoading = true);
|
||||
//if(_hasMore){ // 还有数据可以拉新
|
||||
List newEntries = await mokeHttpRequest();
|
||||
//if (newEntries.isEmpty) {
|
||||
_hasMore = (_pageIndex <= _pageTotal);
|
||||
setState(() {
|
||||
items.addAll(newEntries);
|
||||
isLoading = false;
|
||||
});
|
||||
backElasticEffect();
|
||||
}else if (!isLoading && !_hasMore){ // 这样判断,减少以后的绘制
|
||||
_pageIndex = 0;
|
||||
backElasticEffect();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 伪装吐出新数据
|
||||
* */
|
||||
Future<List> mokeHttpRequest() async {
|
||||
if(widget.requestApi is Function){
|
||||
final listObj = await widget.requestApi({'pageIndex':_pageIndex});
|
||||
_pageIndex = listObj['pageIndex'];
|
||||
_pageTotal = listObj['total'];
|
||||
return listObj['list'];
|
||||
}else {
|
||||
return Future.delayed(Duration(seconds: 2), () {
|
||||
return [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 下拉加载的事件,清空之前list内容,取前X个
|
||||
* 其实就是列表重置
|
||||
* */
|
||||
Future<Null> _handleRefresh() async {
|
||||
List newEntries = await mokeHttpRequest();
|
||||
setState(() {
|
||||
items.clear();
|
||||
items.addAll(newEntries);
|
||||
isLoading = false;
|
||||
_hasMore = true;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* 加载中的提示
|
||||
* */
|
||||
Widget _buildLoadText() {
|
||||
return Container(child: Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: Center(
|
||||
child: Text("数据没有更多了!!!"),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* 上提加载loading的widget,如果数据到达极限,显示没有更多
|
||||
* */
|
||||
Widget _buildProgressIndicator() {
|
||||
if(_hasMore){
|
||||
return new Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: new Center(
|
||||
child:Column(
|
||||
children: <Widget>[
|
||||
new Opacity(
|
||||
opacity: isLoading ? 1.0 : 0.0,
|
||||
child: new CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(Colors.blue)),
|
||||
),
|
||||
SizedBox(height:20.0),
|
||||
Text('稍等片刻更精彩...',style: TextStyle(fontSize: 14.0),)
|
||||
],)
|
||||
//child:
|
||||
),
|
||||
);
|
||||
}else {
|
||||
return _buildLoadText();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_scrollController.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new RefreshIndicator(
|
||||
child:ListView.builder(
|
||||
itemCount: items.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if(index == 0 && index != items.length){
|
||||
return Container(height:0);
|
||||
}
|
||||
if (index == items.length) {
|
||||
//return _buildLoadText();
|
||||
return _buildProgressIndicator();
|
||||
} else {
|
||||
//print('itemsitemsitemsitems:${items[index].title}');
|
||||
//return ListTile(title: Text("Index${index}:${items[index].title}"));
|
||||
if (widget.renderItem is Function) {
|
||||
return widget.renderItem(index,items[index]);
|
||||
}
|
||||
//return makeCard(index,items[index]);
|
||||
}
|
||||
},
|
||||
controller: _scrollController,
|
||||
),
|
||||
onRefresh: _handleRefresh,
|
||||
);
|
||||
}
|
||||
}
|
63
lib-cp/Pagination-copy-copy-copy.dart
Normal file
63
lib-cp/Pagination-copy-copy-copy.dart
Normal file
@ -0,0 +1,63 @@
|
||||
// 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_web/material.dart';
|
||||
import './homeBanner.dart';
|
||||
import '../model/story.dart';
|
||||
import 'package:url_launcher/url_launcher.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://img.alicdn.com/tfs/TB1W4hMAwHqK1RjSZJnXXbNLpXa-519-260.jpg', 'type': 0, 'id': 9695909, 'url': 'https://www.zhihu.com/question/294145797/answer/551162834', 'title': '为什么阿里巴巴、腾讯和 Google 之类的企业都在使用 Flutter 开发 App?'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1XmFIApzqK1RjSZSgXXcpAVXa-720-338.jpg', 'type': 0, 'id': 9695859, 'url': 'https://zhuanlan.zhihu.com/p/51696594', 'title': 'Flutter 1.0 正式发布: Google 的便携 UI 工具包'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1mClCABLoK1RjSZFuXXXn0XXa-600-362.jpg', 'type': 0, 'id': 96956491409, 'url':'https://zhuanlan.zhihu.com/p/53497167','title': 'Flutter 示范应用现已开源 — 万物起源(The History of Everything)'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1fXxIAAvoK1RjSZFNXXcxMVXa-600-362.jpg', 'type': 0, 'id': 9695816, 'url': 'https://mp.weixin.qq.com/s?__biz=MzAwODY4OTk2Mg==&mid=2652048101&idx=1&sn=20296088e4bd8ca33c5c9991167d9f7d&chksm=808caaa0b7fb23b65c0e5806209f8d86da6732f3a00a70353f3606018339518b0a8656f14dc5&mpshare=1&scene=2&srcid=0106SZapVysZdIS6Oc5AhNH6&from=timeline&ascene=2&devicetype=android-27&version=27000038&nettype=WIFI&abtest_cookie=BQABAAgACgALABMAFAAFAJ2GHgAjlx4AV5keAJuZHgCcmR4AAAA%3D&lang=zh_CN&pass_ticket=4K1%2FUpsxP4suPj2iubR17wbAP7r9LW9iYrPAC2dppTqv7j7JO5FWMXtcKeBRxueV&wx_header=1', 'title': 'Flutter 与 Material Design 双剑合璧,助您构建精美应用'}
|
||||
];
|
||||
|
||||
void _launchURL(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _PageSelector(BuildContext context) {
|
||||
List<Widget> list = [];
|
||||
/// super.initState();
|
||||
arr.forEach((item) {
|
||||
bannerStories.add(StoryModel.fromJson(item));
|
||||
});
|
||||
|
||||
|
||||
if (arr.length > 0) {
|
||||
list.add(HomeBanner(bannerStories, (story) {
|
||||
_launchURL('${story.url}');
|
||||
}));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return
|
||||
Column(
|
||||
//physics: AlwaysScrollableScrollPhysics(),
|
||||
//padding: EdgeInsets.only(),
|
||||
children: _PageSelector(context)
|
||||
);
|
||||
}
|
||||
}
|
63
lib-cp/Pagination-copy-copy.dart
Normal file
63
lib-cp/Pagination-copy-copy.dart
Normal file
@ -0,0 +1,63 @@
|
||||
// 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_web/material.dart';
|
||||
import './homeBanner.dart';
|
||||
import '../model/story.dart';
|
||||
import 'package:url_launcher/url_launcher.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://img.alicdn.com/tfs/TB1W4hMAwHqK1RjSZJnXXbNLpXa-519-260.jpg', 'type': 0, 'id': 9695909, 'url': 'https://www.zhihu.com/question/294145797/answer/551162834', 'title': '为什么阿里巴巴、腾讯和 Google 之类的企业都在使用 Flutter 开发 App?'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1XmFIApzqK1RjSZSgXXcpAVXa-720-338.jpg', 'type': 0, 'id': 9695859, 'url': 'https://zhuanlan.zhihu.com/p/51696594', 'title': 'Flutter 1.0 正式发布: Google 的便携 UI 工具包'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1mClCABLoK1RjSZFuXXXn0XXa-600-362.jpg', 'type': 0, 'id': 96956491409, 'url':'https://zhuanlan.zhihu.com/p/53497167','title': 'Flutter 示范应用现已开源 — 万物起源(The History of Everything)'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1fXxIAAvoK1RjSZFNXXcxMVXa-600-362.jpg', 'type': 0, 'id': 9695816, 'url': 'https://mp.weixin.qq.com/s?__biz=MzAwODY4OTk2Mg==&mid=2652048101&idx=1&sn=20296088e4bd8ca33c5c9991167d9f7d&chksm=808caaa0b7fb23b65c0e5806209f8d86da6732f3a00a70353f3606018339518b0a8656f14dc5&mpshare=1&scene=2&srcid=0106SZapVysZdIS6Oc5AhNH6&from=timeline&ascene=2&devicetype=android-27&version=27000038&nettype=WIFI&abtest_cookie=BQABAAgACgALABMAFAAFAJ2GHgAjlx4AV5keAJuZHgCcmR4AAAA%3D&lang=zh_CN&pass_ticket=4K1%2FUpsxP4suPj2iubR17wbAP7r9LW9iYrPAC2dppTqv7j7JO5FWMXtcKeBRxueV&wx_header=1', 'title': 'Flutter 与 Material Design 双剑合璧,助您构建精美应用'}
|
||||
];
|
||||
|
||||
void _launchURL(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _PageSelector(BuildContext context) {
|
||||
List<Widget> list = [];
|
||||
/// super.initState();
|
||||
arr.forEach((item) {
|
||||
bannerStories.add(StoryModel.fromJson(item));
|
||||
});
|
||||
|
||||
|
||||
if (arr.length > 0) {
|
||||
list.add(HomeBanner(bannerStories, (story) {
|
||||
_launchURL('${story.url}');
|
||||
}));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return
|
||||
Column(
|
||||
//physics: AlwaysScrollableScrollPhysics(),
|
||||
//padding: EdgeInsets.only(),
|
||||
children: _PageSelector(context)
|
||||
);
|
||||
}
|
||||
}
|
63
lib-cp/Pagination-copy.dart
Normal file
63
lib-cp/Pagination-copy.dart
Normal file
@ -0,0 +1,63 @@
|
||||
// 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_web/material.dart';
|
||||
import './homeBanner.dart';
|
||||
import '../model/story.dart';
|
||||
import 'package:url_launcher/url_launcher.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://img.alicdn.com/tfs/TB1W4hMAwHqK1RjSZJnXXbNLpXa-519-260.jpg', 'type': 0, 'id': 9695909, 'url': 'https://www.zhihu.com/question/294145797/answer/551162834', 'title': '为什么阿里巴巴、腾讯和 Google 之类的企业都在使用 Flutter 开发 App?'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1XmFIApzqK1RjSZSgXXcpAVXa-720-338.jpg', 'type': 0, 'id': 9695859, 'url': 'https://zhuanlan.zhihu.com/p/51696594', 'title': 'Flutter 1.0 正式发布: Google 的便携 UI 工具包'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1mClCABLoK1RjSZFuXXXn0XXa-600-362.jpg', 'type': 0, 'id': 96956491409, 'url':'https://zhuanlan.zhihu.com/p/53497167','title': 'Flutter 示范应用现已开源 — 万物起源(The History of Everything)'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1fXxIAAvoK1RjSZFNXXcxMVXa-600-362.jpg', 'type': 0, 'id': 9695816, 'url': 'https://mp.weixin.qq.com/s?__biz=MzAwODY4OTk2Mg==&mid=2652048101&idx=1&sn=20296088e4bd8ca33c5c9991167d9f7d&chksm=808caaa0b7fb23b65c0e5806209f8d86da6732f3a00a70353f3606018339518b0a8656f14dc5&mpshare=1&scene=2&srcid=0106SZapVysZdIS6Oc5AhNH6&from=timeline&ascene=2&devicetype=android-27&version=27000038&nettype=WIFI&abtest_cookie=BQABAAgACgALABMAFAAFAJ2GHgAjlx4AV5keAJuZHgCcmR4AAAA%3D&lang=zh_CN&pass_ticket=4K1%2FUpsxP4suPj2iubR17wbAP7r9LW9iYrPAC2dppTqv7j7JO5FWMXtcKeBRxueV&wx_header=1', 'title': 'Flutter 与 Material Design 双剑合璧,助您构建精美应用'}
|
||||
];
|
||||
|
||||
void _launchURL(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _PageSelector(BuildContext context) {
|
||||
List<Widget> list = [];
|
||||
/// super.initState();
|
||||
arr.forEach((item) {
|
||||
bannerStories.add(StoryModel.fromJson(item));
|
||||
});
|
||||
|
||||
|
||||
if (arr.length > 0) {
|
||||
list.add(HomeBanner(bannerStories, (story) {
|
||||
_launchURL('${story.url}');
|
||||
}));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return
|
||||
Column(
|
||||
//physics: AlwaysScrollableScrollPhysics(),
|
||||
//padding: EdgeInsets.only(),
|
||||
children: _PageSelector(context)
|
||||
);
|
||||
}
|
||||
}
|
63
lib-cp/Pagination.dart
Normal file
63
lib-cp/Pagination.dart
Normal file
@ -0,0 +1,63 @@
|
||||
// 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_web/material.dart';
|
||||
import './homeBanner.dart';
|
||||
import '../model/story.dart';
|
||||
import 'package:url_launcher/url_launcher.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://img.alicdn.com/tfs/TB1W4hMAwHqK1RjSZJnXXbNLpXa-519-260.jpg', 'type': 0, 'id': 9695909, 'url': 'https://www.zhihu.com/question/294145797/answer/551162834', 'title': '为什么阿里巴巴、腾讯和 Google 之类的企业都在使用 Flutter 开发 App?'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1XmFIApzqK1RjSZSgXXcpAVXa-720-338.jpg', 'type': 0, 'id': 9695859, 'url': 'https://zhuanlan.zhihu.com/p/51696594', 'title': 'Flutter 1.0 正式发布: Google 的便携 UI 工具包'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1mClCABLoK1RjSZFuXXXn0XXa-600-362.jpg', 'type': 0, 'id': 96956491409, 'url':'https://zhuanlan.zhihu.com/p/53497167','title': 'Flutter 示范应用现已开源 — 万物起源(The History of Everything)'},
|
||||
{'image': 'https://img.alicdn.com/tfs/TB1fXxIAAvoK1RjSZFNXXcxMVXa-600-362.jpg', 'type': 0, 'id': 9695816, 'url': 'https://mp.weixin.qq.com/s?__biz=MzAwODY4OTk2Mg==&mid=2652048101&idx=1&sn=20296088e4bd8ca33c5c9991167d9f7d&chksm=808caaa0b7fb23b65c0e5806209f8d86da6732f3a00a70353f3606018339518b0a8656f14dc5&mpshare=1&scene=2&srcid=0106SZapVysZdIS6Oc5AhNH6&from=timeline&ascene=2&devicetype=android-27&version=27000038&nettype=WIFI&abtest_cookie=BQABAAgACgALABMAFAAFAJ2GHgAjlx4AV5keAJuZHgCcmR4AAAA%3D&lang=zh_CN&pass_ticket=4K1%2FUpsxP4suPj2iubR17wbAP7r9LW9iYrPAC2dppTqv7j7JO5FWMXtcKeBRxueV&wx_header=1', 'title': 'Flutter 与 Material Design 双剑合璧,助您构建精美应用'}
|
||||
];
|
||||
|
||||
void _launchURL(String url) async {
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _PageSelector(BuildContext context) {
|
||||
List<Widget> list = [];
|
||||
/// super.initState();
|
||||
arr.forEach((item) {
|
||||
bannerStories.add(StoryModel.fromJson(item));
|
||||
});
|
||||
|
||||
|
||||
if (arr.length > 0) {
|
||||
list.add(HomeBanner(bannerStories, (story) {
|
||||
_launchURL('${story.url}');
|
||||
}));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return
|
||||
Column(
|
||||
//physics: AlwaysScrollableScrollPhysics(),
|
||||
//padding: EdgeInsets.only(),
|
||||
children: _PageSelector(context)
|
||||
);
|
||||
}
|
||||
}
|
399
lib-cp/SearchInput-copy-copy-copy.dart
Normal file
399
lib-cp/SearchInput-copy-copy-copy.dart
Normal file
@ -0,0 +1,399 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_web/material.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,
|
||||
this.onTap
|
||||
}) : super(key: key);
|
||||
|
||||
final T value;
|
||||
final VoidCallback onTap;
|
||||
final String text;
|
||||
final IconData icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new InkWell(
|
||||
onTap: this.onTap,
|
||||
child: new Container(
|
||||
height: 64.0,
|
||||
padding: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Container(width: 30.0, child: new Icon(icon)) ?? null,
|
||||
new Expanded(child: new Text(text, style: Theme.of(context).textTheme.subhead)),
|
||||
new Text(text, style: Theme.of(context).textTheme.subhead)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
Widget buildBody(List results) {
|
||||
if (_loading) {
|
||||
return new Center(
|
||||
child: new Padding(
|
||||
padding: const EdgeInsets.only(top: 50.0),
|
||||
child: new CircularProgressIndicator()
|
||||
)
|
||||
);
|
||||
}
|
||||
if (results.isNotEmpty) {
|
||||
var content = new SingleChildScrollView(
|
||||
child: new Column(
|
||||
children: results
|
||||
)
|
||||
);
|
||||
return content;
|
||||
}
|
||||
return Center(child: Text("暂无数据"));
|
||||
}
|
||||
|
||||
@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: buildBody(results),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
border: InputBorder.none,
|
||||
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(
|
||||
height: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
borderRadius: BorderRadius.circular(4.0)),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Padding(
|
||||
padding: new EdgeInsets.only(right: 10.0, top: 3.0, left: 10.0),
|
||||
child: new Icon(Icons.search,
|
||||
size: 24.0, color: Theme.of(context).accentColor),
|
||||
),
|
||||
new Expanded(
|
||||
child: new MaterialSearchInput(
|
||||
placeholder: '搜索 flutter 组件',
|
||||
getResults: getResults,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// wigdet干掉.=> componets
|
399
lib-cp/SearchInput-copy-copy.dart
Normal file
399
lib-cp/SearchInput-copy-copy.dart
Normal file
@ -0,0 +1,399 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_web/material.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,
|
||||
this.onTap
|
||||
}) : super(key: key);
|
||||
|
||||
final T value;
|
||||
final VoidCallback onTap;
|
||||
final String text;
|
||||
final IconData icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new InkWell(
|
||||
onTap: this.onTap,
|
||||
child: new Container(
|
||||
height: 64.0,
|
||||
padding: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Container(width: 30.0, child: new Icon(icon)) ?? null,
|
||||
new Expanded(child: new Text(text, style: Theme.of(context).textTheme.subhead)),
|
||||
new Text(text, style: Theme.of(context).textTheme.subhead)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
Widget buildBody(List results) {
|
||||
if (_loading) {
|
||||
return new Center(
|
||||
child: new Padding(
|
||||
padding: const EdgeInsets.only(top: 50.0),
|
||||
child: new CircularProgressIndicator()
|
||||
)
|
||||
);
|
||||
}
|
||||
if (results.isNotEmpty) {
|
||||
var content = new SingleChildScrollView(
|
||||
child: new Column(
|
||||
children: results
|
||||
)
|
||||
);
|
||||
return content;
|
||||
}
|
||||
return Center(child: Text("暂无数据"));
|
||||
}
|
||||
|
||||
@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: buildBody(results),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
border: InputBorder.none,
|
||||
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(
|
||||
height: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
borderRadius: BorderRadius.circular(4.0)),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Padding(
|
||||
padding: new EdgeInsets.only(right: 10.0, top: 3.0, left: 10.0),
|
||||
child: new Icon(Icons.search,
|
||||
size: 24.0, color: Theme.of(context).accentColor),
|
||||
),
|
||||
new Expanded(
|
||||
child: new MaterialSearchInput(
|
||||
placeholder: '搜索 flutter 组件',
|
||||
getResults: getResults,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// wigdet干掉.=> componets
|
399
lib-cp/SearchInput-copy.dart
Normal file
399
lib-cp/SearchInput-copy.dart
Normal file
@ -0,0 +1,399 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_web/material.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,
|
||||
this.onTap
|
||||
}) : super(key: key);
|
||||
|
||||
final T value;
|
||||
final VoidCallback onTap;
|
||||
final String text;
|
||||
final IconData icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new InkWell(
|
||||
onTap: this.onTap,
|
||||
child: new Container(
|
||||
height: 64.0,
|
||||
padding: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Container(width: 30.0, child: new Icon(icon)) ?? null,
|
||||
new Expanded(child: new Text(text, style: Theme.of(context).textTheme.subhead)),
|
||||
new Text(text, style: Theme.of(context).textTheme.subhead)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
Widget buildBody(List results) {
|
||||
if (_loading) {
|
||||
return new Center(
|
||||
child: new Padding(
|
||||
padding: const EdgeInsets.only(top: 50.0),
|
||||
child: new CircularProgressIndicator()
|
||||
)
|
||||
);
|
||||
}
|
||||
if (results.isNotEmpty) {
|
||||
var content = new SingleChildScrollView(
|
||||
child: new Column(
|
||||
children: results
|
||||
)
|
||||
);
|
||||
return content;
|
||||
}
|
||||
return Center(child: Text("暂无数据"));
|
||||
}
|
||||
|
||||
@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: buildBody(results),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
border: InputBorder.none,
|
||||
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(
|
||||
height: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
borderRadius: BorderRadius.circular(4.0)),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Padding(
|
||||
padding: new EdgeInsets.only(right: 10.0, top: 3.0, left: 10.0),
|
||||
child: new Icon(Icons.search,
|
||||
size: 24.0, color: Theme.of(context).accentColor),
|
||||
),
|
||||
new Expanded(
|
||||
child: new MaterialSearchInput(
|
||||
placeholder: '搜索 flutter 组件',
|
||||
getResults: getResults,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// wigdet干掉.=> componets
|
399
lib-cp/SearchInput.dart
Normal file
399
lib-cp/SearchInput.dart
Normal file
@ -0,0 +1,399 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_web/material.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,
|
||||
this.onTap
|
||||
}) : super(key: key);
|
||||
|
||||
final T value;
|
||||
final VoidCallback onTap;
|
||||
final String text;
|
||||
final IconData icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new InkWell(
|
||||
onTap: this.onTap,
|
||||
child: new Container(
|
||||
height: 64.0,
|
||||
padding: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Container(width: 30.0, child: new Icon(icon)) ?? null,
|
||||
new Expanded(child: new Text(text, style: Theme.of(context).textTheme.subhead)),
|
||||
new Text(text, style: Theme.of(context).textTheme.subhead)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
Widget buildBody(List results) {
|
||||
if (_loading) {
|
||||
return new Center(
|
||||
child: new Padding(
|
||||
padding: const EdgeInsets.only(top: 50.0),
|
||||
child: new CircularProgressIndicator()
|
||||
)
|
||||
);
|
||||
}
|
||||
if (results.isNotEmpty) {
|
||||
var content = new SingleChildScrollView(
|
||||
child: new Column(
|
||||
children: results
|
||||
)
|
||||
);
|
||||
return content;
|
||||
}
|
||||
return Center(child: Text("暂无数据"));
|
||||
}
|
||||
|
||||
@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: buildBody(results),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
border: InputBorder.none,
|
||||
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(
|
||||
height: 40.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
borderRadius: BorderRadius.circular(4.0)),
|
||||
child: new Row(
|
||||
children: <Widget>[
|
||||
new Padding(
|
||||
padding: new EdgeInsets.only(right: 10.0, top: 3.0, left: 10.0),
|
||||
child: new Icon(Icons.search,
|
||||
size: 24.0, color: Theme.of(context).accentColor),
|
||||
),
|
||||
new Expanded(
|
||||
child: new MaterialSearchInput(
|
||||
placeholder: '搜索 flutter 组件',
|
||||
getResults: getResults,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// wigdet干掉.=> componets
|
140
lib-cp/homeBanner-copy-copy-copy.dart
Normal file
140
lib-cp/homeBanner-copy-copy-copy.dart
Normal file
@ -0,0 +1,140 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_web/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: 18.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);
|
140
lib-cp/homeBanner-copy-copy.dart
Normal file
140
lib-cp/homeBanner-copy-copy.dart
Normal file
@ -0,0 +1,140 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_web/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: 18.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);
|
140
lib-cp/homeBanner-copy.dart
Normal file
140
lib-cp/homeBanner-copy.dart
Normal file
@ -0,0 +1,140 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_web/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: 18.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);
|
140
lib-cp/homeBanner.dart
Normal file
140
lib-cp/homeBanner.dart
Normal file
@ -0,0 +1,140 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_web/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: 18.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);
|
32
lib-cp/markdown-copy-copy-copy.dart
Normal file
32
lib-cp/markdown-copy-copy-copy.dart
Normal file
@ -0,0 +1,32 @@
|
||||
import 'package:flutter_markdown/flutter_markdown.dart' as md;
|
||||
import '../common/high_light_code.dart';
|
||||
import 'package:flutter_web/material.dart';
|
||||
|
||||
/// 使用方法
|
||||
/// MarkdownBody(markdown)
|
||||
final hightlighter = new HighLight();
|
||||
class HighLight extends md.SyntaxHighlighter {
|
||||
@override
|
||||
TextSpan format(String source) {
|
||||
// TODO: implement format
|
||||
final SyntaxHighlighterStyle style = SyntaxHighlighterStyle.lightThemeStyle();
|
||||
return TextSpan(
|
||||
style: const TextStyle(fontSize: 10.0),
|
||||
children: <TextSpan>[
|
||||
DartSyntaxHighlighter(style).format(source)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MarkdownBody extends StatelessWidget {
|
||||
String data;
|
||||
MarkdownBody(this.data);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement build
|
||||
return md.MarkdownBody(data: data, syntaxHighlighter: new HighLight());
|
||||
}
|
||||
}
|
32
lib-cp/markdown-copy-copy.dart
Normal file
32
lib-cp/markdown-copy-copy.dart
Normal file
@ -0,0 +1,32 @@
|
||||
import 'package:flutter_markdown/flutter_markdown.dart' as md;
|
||||
import '../common/high_light_code.dart';
|
||||
import 'package:flutter_web/material.dart';
|
||||
|
||||
/// 使用方法
|
||||
/// MarkdownBody(markdown)
|
||||
final hightlighter = new HighLight();
|
||||
class HighLight extends md.SyntaxHighlighter {
|
||||
@override
|
||||
TextSpan format(String source) {
|
||||
// TODO: implement format
|
||||
final SyntaxHighlighterStyle style = SyntaxHighlighterStyle.lightThemeStyle();
|
||||
return TextSpan(
|
||||
style: const TextStyle(fontSize: 10.0),
|
||||
children: <TextSpan>[
|
||||
DartSyntaxHighlighter(style).format(source)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MarkdownBody extends StatelessWidget {
|
||||
String data;
|
||||
MarkdownBody(this.data);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement build
|
||||
return md.MarkdownBody(data: data, syntaxHighlighter: new HighLight());
|
||||
}
|
||||
}
|
32
lib-cp/markdown-copy.dart
Normal file
32
lib-cp/markdown-copy.dart
Normal file
@ -0,0 +1,32 @@
|
||||
import 'package:flutter_markdown/flutter_markdown.dart' as md;
|
||||
import '../common/high_light_code.dart';
|
||||
import 'package:flutter_web/material.dart';
|
||||
|
||||
/// 使用方法
|
||||
/// MarkdownBody(markdown)
|
||||
final hightlighter = new HighLight();
|
||||
class HighLight extends md.SyntaxHighlighter {
|
||||
@override
|
||||
TextSpan format(String source) {
|
||||
// TODO: implement format
|
||||
final SyntaxHighlighterStyle style = SyntaxHighlighterStyle.lightThemeStyle();
|
||||
return TextSpan(
|
||||
style: const TextStyle(fontSize: 10.0),
|
||||
children: <TextSpan>[
|
||||
DartSyntaxHighlighter(style).format(source)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MarkdownBody extends StatelessWidget {
|
||||
String data;
|
||||
MarkdownBody(this.data);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement build
|
||||
return md.MarkdownBody(data: data, syntaxHighlighter: new HighLight());
|
||||
}
|
||||
}
|
32
lib-cp/markdown.dart
Normal file
32
lib-cp/markdown.dart
Normal file
@ -0,0 +1,32 @@
|
||||
import 'package:flutter_markdown/flutter_markdown.dart' as md;
|
||||
import '../common/high_light_code.dart';
|
||||
import 'package:flutter_web/material.dart';
|
||||
|
||||
/// 使用方法
|
||||
/// MarkdownBody(markdown)
|
||||
final hightlighter = new HighLight();
|
||||
class HighLight extends md.SyntaxHighlighter {
|
||||
@override
|
||||
TextSpan format(String source) {
|
||||
// TODO: implement format
|
||||
final SyntaxHighlighterStyle style = SyntaxHighlighterStyle.lightThemeStyle();
|
||||
return TextSpan(
|
||||
style: const TextStyle(fontSize: 10.0),
|
||||
children: <TextSpan>[
|
||||
DartSyntaxHighlighter(style).format(source)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MarkdownBody extends StatelessWidget {
|
||||
String data;
|
||||
MarkdownBody(this.data);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement build
|
||||
return md.MarkdownBody(data: data, syntaxHighlighter: new HighLight());
|
||||
}
|
||||
}
|
88
lib-cp/widget_item-copy-copy-copy.dart
Normal file
88
lib-cp/widget_item-copy-copy-copy.dart
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @author 一凨
|
||||
*/
|
||||
import 'package:flutter_web/material.dart';
|
||||
import '../common/Style.dart';
|
||||
import '../common/widget_name_to_icon.dart';
|
||||
|
||||
class WidgetItem extends StatelessWidget {
|
||||
final String title;
|
||||
final VoidCallback onTap;
|
||||
final int index; //用于计算border
|
||||
final int totalCount;
|
||||
final int rowLength;
|
||||
String _widgetName;
|
||||
|
||||
WidgetItem(
|
||||
{this.title, this.onTap, this.index, this.totalCount, this.rowLength});
|
||||
|
||||
Border _buildBorder(context) {
|
||||
Border _border;
|
||||
bool isRight = (index % rowLength) == (rowLength - 1); //是不是最右边的,决定是否有右侧边框
|
||||
var currentRow = (index + 1) % rowLength > 0
|
||||
? (index + 1) ~/ rowLength + 1
|
||||
: (index + 1) ~/ rowLength;
|
||||
int totalRow = totalCount % rowLength > 0
|
||||
? totalCount ~/ rowLength + 1
|
||||
: totalCount ~/ rowLength;
|
||||
if (currentRow < totalRow && isRight) {
|
||||
//不是最后一行,是最右边
|
||||
_border = Border(
|
||||
bottom: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
if (currentRow < totalRow && !isRight) {
|
||||
_border = Border(
|
||||
right: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
bottom: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
if (currentRow == totalRow && !isRight) {
|
||||
_border = Border(
|
||||
right: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
return _border;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_widgetName = title.replaceFirst(
|
||||
//首字母转为大写
|
||||
title.substring(0, 1),
|
||||
title.substring(0, 1).toUpperCase());
|
||||
Icon widgetIcon;
|
||||
if (WidgetName2Icon.icons[_widgetName] != null) {
|
||||
widgetIcon = Icon(WidgetName2Icon.icons[_widgetName]);
|
||||
} else {
|
||||
widgetIcon = Icon(
|
||||
Icons.crop,
|
||||
);
|
||||
}
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
decoration: new BoxDecoration(
|
||||
border: _buildBorder(context),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 30.0, horizontal: 10.0),
|
||||
height: 150.0,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
widgetIcon,
|
||||
SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Text(_widgetName),
|
||||
],
|
||||
) ,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
88
lib-cp/widget_item-copy-copy.dart
Normal file
88
lib-cp/widget_item-copy-copy.dart
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @author 一凨
|
||||
*/
|
||||
import 'package:flutter_web/material.dart';
|
||||
import '../common/Style.dart';
|
||||
import '../common/widget_name_to_icon.dart';
|
||||
|
||||
class WidgetItem extends StatelessWidget {
|
||||
final String title;
|
||||
final VoidCallback onTap;
|
||||
final int index; //用于计算border
|
||||
final int totalCount;
|
||||
final int rowLength;
|
||||
String _widgetName;
|
||||
|
||||
WidgetItem(
|
||||
{this.title, this.onTap, this.index, this.totalCount, this.rowLength});
|
||||
|
||||
Border _buildBorder(context) {
|
||||
Border _border;
|
||||
bool isRight = (index % rowLength) == (rowLength - 1); //是不是最右边的,决定是否有右侧边框
|
||||
var currentRow = (index + 1) % rowLength > 0
|
||||
? (index + 1) ~/ rowLength + 1
|
||||
: (index + 1) ~/ rowLength;
|
||||
int totalRow = totalCount % rowLength > 0
|
||||
? totalCount ~/ rowLength + 1
|
||||
: totalCount ~/ rowLength;
|
||||
if (currentRow < totalRow && isRight) {
|
||||
//不是最后一行,是最右边
|
||||
_border = Border(
|
||||
bottom: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
if (currentRow < totalRow && !isRight) {
|
||||
_border = Border(
|
||||
right: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
bottom: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
if (currentRow == totalRow && !isRight) {
|
||||
_border = Border(
|
||||
right: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
return _border;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_widgetName = title.replaceFirst(
|
||||
//首字母转为大写
|
||||
title.substring(0, 1),
|
||||
title.substring(0, 1).toUpperCase());
|
||||
Icon widgetIcon;
|
||||
if (WidgetName2Icon.icons[_widgetName] != null) {
|
||||
widgetIcon = Icon(WidgetName2Icon.icons[_widgetName]);
|
||||
} else {
|
||||
widgetIcon = Icon(
|
||||
Icons.crop,
|
||||
);
|
||||
}
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
decoration: new BoxDecoration(
|
||||
border: _buildBorder(context),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 30.0, horizontal: 10.0),
|
||||
height: 150.0,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
widgetIcon,
|
||||
SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Text(_widgetName),
|
||||
],
|
||||
) ,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
88
lib-cp/widget_item-copy.dart
Normal file
88
lib-cp/widget_item-copy.dart
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @author 一凨
|
||||
*/
|
||||
import 'package:flutter_web/material.dart';
|
||||
import '../common/Style.dart';
|
||||
import '../common/widget_name_to_icon.dart';
|
||||
|
||||
class WidgetItem extends StatelessWidget {
|
||||
final String title;
|
||||
final VoidCallback onTap;
|
||||
final int index; //用于计算border
|
||||
final int totalCount;
|
||||
final int rowLength;
|
||||
String _widgetName;
|
||||
|
||||
WidgetItem(
|
||||
{this.title, this.onTap, this.index, this.totalCount, this.rowLength});
|
||||
|
||||
Border _buildBorder(context) {
|
||||
Border _border;
|
||||
bool isRight = (index % rowLength) == (rowLength - 1); //是不是最右边的,决定是否有右侧边框
|
||||
var currentRow = (index + 1) % rowLength > 0
|
||||
? (index + 1) ~/ rowLength + 1
|
||||
: (index + 1) ~/ rowLength;
|
||||
int totalRow = totalCount % rowLength > 0
|
||||
? totalCount ~/ rowLength + 1
|
||||
: totalCount ~/ rowLength;
|
||||
if (currentRow < totalRow && isRight) {
|
||||
//不是最后一行,是最右边
|
||||
_border = Border(
|
||||
bottom: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
if (currentRow < totalRow && !isRight) {
|
||||
_border = Border(
|
||||
right: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
bottom: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
if (currentRow == totalRow && !isRight) {
|
||||
_border = Border(
|
||||
right: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
return _border;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_widgetName = title.replaceFirst(
|
||||
//首字母转为大写
|
||||
title.substring(0, 1),
|
||||
title.substring(0, 1).toUpperCase());
|
||||
Icon widgetIcon;
|
||||
if (WidgetName2Icon.icons[_widgetName] != null) {
|
||||
widgetIcon = Icon(WidgetName2Icon.icons[_widgetName]);
|
||||
} else {
|
||||
widgetIcon = Icon(
|
||||
Icons.crop,
|
||||
);
|
||||
}
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
decoration: new BoxDecoration(
|
||||
border: _buildBorder(context),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 30.0, horizontal: 10.0),
|
||||
height: 150.0,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
widgetIcon,
|
||||
SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Text(_widgetName),
|
||||
],
|
||||
) ,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
88
lib-cp/widget_item.dart
Normal file
88
lib-cp/widget_item.dart
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @author 一凨
|
||||
*/
|
||||
import 'package:flutter_web/material.dart';
|
||||
import '../common/Style.dart';
|
||||
import '../common/widget_name_to_icon.dart';
|
||||
|
||||
class WidgetItem extends StatelessWidget {
|
||||
final String title;
|
||||
final VoidCallback onTap;
|
||||
final int index; //用于计算border
|
||||
final int totalCount;
|
||||
final int rowLength;
|
||||
String _widgetName;
|
||||
|
||||
WidgetItem(
|
||||
{this.title, this.onTap, this.index, this.totalCount, this.rowLength});
|
||||
|
||||
Border _buildBorder(context) {
|
||||
Border _border;
|
||||
bool isRight = (index % rowLength) == (rowLength - 1); //是不是最右边的,决定是否有右侧边框
|
||||
var currentRow = (index + 1) % rowLength > 0
|
||||
? (index + 1) ~/ rowLength + 1
|
||||
: (index + 1) ~/ rowLength;
|
||||
int totalRow = totalCount % rowLength > 0
|
||||
? totalCount ~/ rowLength + 1
|
||||
: totalCount ~/ rowLength;
|
||||
if (currentRow < totalRow && isRight) {
|
||||
//不是最后一行,是最右边
|
||||
_border = Border(
|
||||
bottom: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
if (currentRow < totalRow && !isRight) {
|
||||
_border = Border(
|
||||
right: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
bottom: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
if (currentRow == totalRow && !isRight) {
|
||||
_border = Border(
|
||||
right: const BorderSide(
|
||||
width: 1.0, color: Color(WidgetDemoColor.borderColor)),
|
||||
);
|
||||
}
|
||||
return _border;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_widgetName = title.replaceFirst(
|
||||
//首字母转为大写
|
||||
title.substring(0, 1),
|
||||
title.substring(0, 1).toUpperCase());
|
||||
Icon widgetIcon;
|
||||
if (WidgetName2Icon.icons[_widgetName] != null) {
|
||||
widgetIcon = Icon(WidgetName2Icon.icons[_widgetName]);
|
||||
} else {
|
||||
widgetIcon = Icon(
|
||||
Icons.crop,
|
||||
);
|
||||
}
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
decoration: new BoxDecoration(
|
||||
border: _buildBorder(context),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 30.0, horizontal: 10.0),
|
||||
height: 150.0,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
widgetIcon,
|
||||
SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Text(_widgetName),
|
||||
],
|
||||
) ,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
80
lib-cp/widget_item_container-copy-copy-copy.dart
Normal file
80
lib-cp/widget_item_container-copy-copy-copy.dart
Normal file
@ -0,0 +1,80 @@
|
||||
import 'package:flutter_web/material.dart';
|
||||
import './widget_item.dart';
|
||||
import '../routers/application.dart';
|
||||
import '../widgets/index.dart';
|
||||
|
||||
class WidgetItemContainer extends StatelessWidget {
|
||||
final int columnCount; //一行几个
|
||||
final List<dynamic> categories;
|
||||
final bool isWidgetPoint;
|
||||
// 所有的可用demos;
|
||||
List widgetDemosList = new WidgetDemoList().getDemos();
|
||||
|
||||
WidgetItemContainer(
|
||||
{Key key,
|
||||
@required this.categories,
|
||||
@required this.columnCount,
|
||||
@required this.isWidgetPoint})
|
||||
: super(key: key);
|
||||
|
||||
List<Widget> _buildColumns(context) {
|
||||
List<Widget> _listWidget = [];
|
||||
List<Widget> _listRows = [];
|
||||
int addI;
|
||||
for (int i = 0, length = categories.length; i < length; i += columnCount) {
|
||||
_listRows = [];
|
||||
for (int innerI = 0; innerI < columnCount; innerI++) {
|
||||
addI = innerI + i;
|
||||
if (addI < length) {
|
||||
dynamic item = categories[addI];
|
||||
_listRows.add(
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: WidgetItem(
|
||||
title: item.name,
|
||||
onTap: () {
|
||||
if (isWidgetPoint) {
|
||||
String targetName = item.name;
|
||||
String targetRouter = '/category/error/404';
|
||||
widgetDemosList.forEach((item) {
|
||||
if (item.name == targetName) {
|
||||
targetRouter = item.routerName;
|
||||
}
|
||||
});
|
||||
Application.router.navigateTo(context, "${targetRouter}");
|
||||
} else {
|
||||
Application.router
|
||||
.navigateTo(context, "/category/${item.name}");
|
||||
}
|
||||
},
|
||||
index: addI,
|
||||
totalCount: length,
|
||||
rowLength: columnCount,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
_listRows.add(
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
_listWidget.add(
|
||||
Row(
|
||||
children: _listRows,
|
||||
),
|
||||
);
|
||||
}
|
||||
return _listWidget;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: _buildColumns(context),
|
||||
);
|
||||
}
|
||||
}
|
80
lib-cp/widget_item_container-copy-copy.dart
Normal file
80
lib-cp/widget_item_container-copy-copy.dart
Normal file
@ -0,0 +1,80 @@
|
||||
import 'package:flutter_web/material.dart';
|
||||
import './widget_item.dart';
|
||||
import '../routers/application.dart';
|
||||
import '../widgets/index.dart';
|
||||
|
||||
class WidgetItemContainer extends StatelessWidget {
|
||||
final int columnCount; //一行几个
|
||||
final List<dynamic> categories;
|
||||
final bool isWidgetPoint;
|
||||
// 所有的可用demos;
|
||||
List widgetDemosList = new WidgetDemoList().getDemos();
|
||||
|
||||
WidgetItemContainer(
|
||||
{Key key,
|
||||
@required this.categories,
|
||||
@required this.columnCount,
|
||||
@required this.isWidgetPoint})
|
||||
: super(key: key);
|
||||
|
||||
List<Widget> _buildColumns(context) {
|
||||
List<Widget> _listWidget = [];
|
||||
List<Widget> _listRows = [];
|
||||
int addI;
|
||||
for (int i = 0, length = categories.length; i < length; i += columnCount) {
|
||||
_listRows = [];
|
||||
for (int innerI = 0; innerI < columnCount; innerI++) {
|
||||
addI = innerI + i;
|
||||
if (addI < length) {
|
||||
dynamic item = categories[addI];
|
||||
_listRows.add(
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: WidgetItem(
|
||||
title: item.name,
|
||||
onTap: () {
|
||||
if (isWidgetPoint) {
|
||||
String targetName = item.name;
|
||||
String targetRouter = '/category/error/404';
|
||||
widgetDemosList.forEach((item) {
|
||||
if (item.name == targetName) {
|
||||
targetRouter = item.routerName;
|
||||
}
|
||||
});
|
||||
Application.router.navigateTo(context, "${targetRouter}");
|
||||
} else {
|
||||
Application.router
|
||||
.navigateTo(context, "/category/${item.name}");
|
||||
}
|
||||
},
|
||||
index: addI,
|
||||
totalCount: length,
|
||||
rowLength: columnCount,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
_listRows.add(
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
_listWidget.add(
|
||||
Row(
|
||||
children: _listRows,
|
||||
),
|
||||
);
|
||||
}
|
||||
return _listWidget;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: _buildColumns(context),
|
||||
);
|
||||
}
|
||||
}
|
80
lib-cp/widget_item_container-copy.dart
Normal file
80
lib-cp/widget_item_container-copy.dart
Normal file
@ -0,0 +1,80 @@
|
||||
import 'package:flutter_web/material.dart';
|
||||
import './widget_item.dart';
|
||||
import '../routers/application.dart';
|
||||
import '../widgets/index.dart';
|
||||
|
||||
class WidgetItemContainer extends StatelessWidget {
|
||||
final int columnCount; //一行几个
|
||||
final List<dynamic> categories;
|
||||
final bool isWidgetPoint;
|
||||
// 所有的可用demos;
|
||||
List widgetDemosList = new WidgetDemoList().getDemos();
|
||||
|
||||
WidgetItemContainer(
|
||||
{Key key,
|
||||
@required this.categories,
|
||||
@required this.columnCount,
|
||||
@required this.isWidgetPoint})
|
||||
: super(key: key);
|
||||
|
||||
List<Widget> _buildColumns(context) {
|
||||
List<Widget> _listWidget = [];
|
||||
List<Widget> _listRows = [];
|
||||
int addI;
|
||||
for (int i = 0, length = categories.length; i < length; i += columnCount) {
|
||||
_listRows = [];
|
||||
for (int innerI = 0; innerI < columnCount; innerI++) {
|
||||
addI = innerI + i;
|
||||
if (addI < length) {
|
||||
dynamic item = categories[addI];
|
||||
_listRows.add(
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: WidgetItem(
|
||||
title: item.name,
|
||||
onTap: () {
|
||||
if (isWidgetPoint) {
|
||||
String targetName = item.name;
|
||||
String targetRouter = '/category/error/404';
|
||||
widgetDemosList.forEach((item) {
|
||||
if (item.name == targetName) {
|
||||
targetRouter = item.routerName;
|
||||
}
|
||||
});
|
||||
Application.router.navigateTo(context, "${targetRouter}");
|
||||
} else {
|
||||
Application.router
|
||||
.navigateTo(context, "/category/${item.name}");
|
||||
}
|
||||
},
|
||||
index: addI,
|
||||
totalCount: length,
|
||||
rowLength: columnCount,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
_listRows.add(
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
_listWidget.add(
|
||||
Row(
|
||||
children: _listRows,
|
||||
),
|
||||
);
|
||||
}
|
||||
return _listWidget;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: _buildColumns(context),
|
||||
);
|
||||
}
|
||||
}
|
80
lib-cp/widget_item_container.dart
Normal file
80
lib-cp/widget_item_container.dart
Normal file
@ -0,0 +1,80 @@
|
||||
import 'package:flutter_web/material.dart';
|
||||
import './widget_item.dart';
|
||||
import '../routers/application.dart';
|
||||
import '../widgets/index.dart';
|
||||
|
||||
class WidgetItemContainer extends StatelessWidget {
|
||||
final int columnCount; //一行几个
|
||||
final List<dynamic> categories;
|
||||
final bool isWidgetPoint;
|
||||
// 所有的可用demos;
|
||||
List widgetDemosList = new WidgetDemoList().getDemos();
|
||||
|
||||
WidgetItemContainer(
|
||||
{Key key,
|
||||
@required this.categories,
|
||||
@required this.columnCount,
|
||||
@required this.isWidgetPoint})
|
||||
: super(key: key);
|
||||
|
||||
List<Widget> _buildColumns(context) {
|
||||
List<Widget> _listWidget = [];
|
||||
List<Widget> _listRows = [];
|
||||
int addI;
|
||||
for (int i = 0, length = categories.length; i < length; i += columnCount) {
|
||||
_listRows = [];
|
||||
for (int innerI = 0; innerI < columnCount; innerI++) {
|
||||
addI = innerI + i;
|
||||
if (addI < length) {
|
||||
dynamic item = categories[addI];
|
||||
_listRows.add(
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: WidgetItem(
|
||||
title: item.name,
|
||||
onTap: () {
|
||||
if (isWidgetPoint) {
|
||||
String targetName = item.name;
|
||||
String targetRouter = '/category/error/404';
|
||||
widgetDemosList.forEach((item) {
|
||||
if (item.name == targetName) {
|
||||
targetRouter = item.routerName;
|
||||
}
|
||||
});
|
||||
Application.router.navigateTo(context, "${targetRouter}");
|
||||
} else {
|
||||
Application.router
|
||||
.navigateTo(context, "/category/${item.name}");
|
||||
}
|
||||
},
|
||||
index: addI,
|
||||
totalCount: length,
|
||||
rowLength: columnCount,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
_listRows.add(
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
_listWidget.add(
|
||||
Row(
|
||||
children: _listRows,
|
||||
),
|
||||
);
|
||||
}
|
||||
return _listWidget;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: _buildColumns(context),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/**
|
||||
* Created with Android Studio.
|
||||
* User: 一晟
|
||||
* Date: 2019/6/9
|
||||
* Time: 9:01 AM
|
||||
* email: zhu.yan@alibaba-inc.com
|
||||
* tartget:
|
||||
*/
|
||||
import 'dart:io'; // io作为dart标准库,用来操作文件,不需要在pubspec.yaml中声明依赖
|
||||
import 'package:args/args.dart';
|
||||
|
||||
ArgResults argResults;
|
||||
|
||||
void main(List<String> arguments) {
|
||||
final ArgParser argParser = new ArgParser()
|
||||
..addOption('title', abbr: 't',
|
||||
help: "The title will be inserted into the <title> tag.")
|
||||
..addOption('filename', abbr: 'f', defaultsTo: 'index.html',
|
||||
help: "Optional. Output file name. (Default: index.html)");
|
||||
|
||||
argResults = argParser.parse(arguments);
|
||||
|
||||
final String title = argResults['title'];
|
||||
|
||||
if (title == null) {
|
||||
handleError("Missing required argument: title");
|
||||
}
|
||||
else {
|
||||
final String filename = argResults['filename'];
|
||||
final String output = """<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>$title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
|
||||
new File(filename).writeAsStringSync(output);// 为了简单实用了同步版本
|
||||
stdout.writeln("File saved: $filename");
|
||||
}
|
||||
}
|
||||
|
||||
// 异步输出错误信息到标准错误流
|
||||
void handleError(String msg) {
|
||||
stderr.writeln(msg);
|
||||
exitCode = 2; //当程序退出,虚拟机检查exitCode,0 表示Success,1 表示Warnings,2 表示Errors
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Created with Android Studio.
|
||||
* User: 一晟
|
||||
* Date: 2019/6/9
|
||||
* Time: 10:15 AM
|
||||
* email: zhu.yan@alibaba-inc.com
|
||||
* tartget:
|
||||
*/
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:dart_inquirer/dart_inquirer.dart';
|
||||
import 'package:console/console.dart';
|
||||
|
||||
//const defaultPath = "/Users/ryan/work/ali/github/flutter-study/flutter_web/examples/@ali-flutter-go/lib/widgets/elements";
|
||||
const defaultPath = "/Users/ryan/work/ali/github/flutter-project/flutter-go-web/examples/@ali-flutter-go/lib/widgets/themes";
|
||||
|
||||
void main() async{
|
||||
qus_ans();
|
||||
}
|
||||
|
||||
void qus_ans() async{
|
||||
List<Question> questions = [
|
||||
ConfirmQuestion('confirm', '使用输入的文件目录 ?',preferN: false),
|
||||
InputQuestion('path', '输入文件目录:', skipIf: (Map ctx) => ctx["confirm"] == false),
|
||||
ConfirmQuestion('default', '使用默认文件目录 ?', skipIf: (Map ctx) => ctx["confirm"] == true)
|
||||
];
|
||||
Prompt prompt = Prompt(questions);
|
||||
Map answers = await prompt.execute();
|
||||
String filePath = answers["path"];
|
||||
|
||||
print(Seperator('='));
|
||||
/// print(answers);
|
||||
|
||||
if (answers["path"] is String && (answers["path"] as String).isNotEmpty){
|
||||
print("使用输入文件目录:${filePath}");
|
||||
printFilesAync(filePath);
|
||||
}else if(!answers["confirm"] && answers["default"]){
|
||||
print("使用默认文件目录:${defaultPath}");
|
||||
printFilesAync(defaultPath);
|
||||
}else{
|
||||
print(format("{@red}放弃转换!{@normal}"));
|
||||
}
|
||||
}
|
||||
|
||||
void printFilesAync(String path) async{
|
||||
try{
|
||||
var directory = new Directory(path);
|
||||
List<FileSystemEntity> files = directory.listSync();
|
||||
if(files.length == 0) {
|
||||
print(format("{@yellow}目录为空!{@normal}"));
|
||||
return;
|
||||
}
|
||||
//print('2::${files}');
|
||||
for(var f in files){
|
||||
var bool = FileSystemEntity.isFileSync(f.path);
|
||||
if(!bool){
|
||||
//print('1::${bool}::${f.path}::${files.length}');
|
||||
await printFilesAync(f.path);
|
||||
}else if(f.path.indexOf('.dart')!=-1){
|
||||
print('2::${bool}::${f.path}::${files.length}');
|
||||
var newPath = (f.path).replaceAll(new RegExp(r'.dart'), '-copy.dart');
|
||||
File newfile = new File(newPath);
|
||||
File oldfile = new File(f.path);
|
||||
|
||||
List<String> lines = oldfile.readAsLinesSync();
|
||||
|
||||
var result = '';
|
||||
lines.forEach((l)=>{
|
||||
if(l.trim().indexOf('package:flutter/material.dart') >=0){
|
||||
result += "import 'package:flutter_web/material.dart';\n"
|
||||
}else if(l.trim().indexOf('package:flutter/widgets.dart') >=0){
|
||||
result += "import 'package:flutter_web/widgets.dart';\n"
|
||||
}else if(l.trim().indexOf('package:flutter/cupertino.dart') >=0){
|
||||
result += "import 'package:flutter_web/cupertino.dart';\n"
|
||||
}else if(l.trim().indexOf('package:flutter/services.dart') >=0){
|
||||
result += "import 'package:flutter_web/services.dart';\n"
|
||||
}else if(l.trim().indexOf('package:flutter/rendering.dart') >=0){
|
||||
result += "import 'package:flutter_web/rendering.dart';\n"
|
||||
}else if(l.trim().indexOf('dart:ui') >=0){
|
||||
result += "import 'package:flutter_web_ui/ui.dart';\n"
|
||||
}else {
|
||||
result += '${l}\n'
|
||||
}
|
||||
}
|
||||
);
|
||||
oldfile.writeAsStringSync(result);
|
||||
//newfile.writeAsStringSync(result);
|
||||
///await newfile.create();
|
||||
//await newfile.create();
|
||||
///file.createSync();
|
||||
///await oldfile.delete();
|
||||
}
|
||||
}
|
||||
}catch(e){
|
||||
print(format("{@red}目录不存在!{@normal}"));
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Created with Android Studio.
|
||||
* User: 一晟
|
||||
* Date: 2019/6/9
|
||||
* Time: 8:16 AM
|
||||
* email: zhu.yan@alibaba-inc.com
|
||||
* tartget:
|
||||
*/
|
||||
import 'package:args/args.dart'; // 使用其中两个类ArgParser和ArgResults
|
||||
|
||||
ArgResults argResults; // 声明ArgResults类型的全局变量,保存解析的参数结果
|
||||
// 同时,argResults也是ArgResults的实例
|
||||
|
||||
void main(List<String> arguments) {
|
||||
// 创建ArgParser的实例,同时指定需要输入的参数
|
||||
final ArgParser argParser = new ArgParser()
|
||||
/// “help”参数通过addFlag()定义;flag 是一个特殊的命令行参数,它是一个Boolean值而不是String。如果flag在命令行中出现,它的值是true。
|
||||
..addOption('name', abbr: 'n', defaultsTo: 'World', help: "Who would you like to greet?") // abbr表示缩写或别名,defaultsTo表示默认值
|
||||
..addFlag('help', abbr: 'h', negatable: false, help: "Displays this help information.");
|
||||
|
||||
argResults = argParser.parse(arguments);
|
||||
if (argResults['help']) {
|
||||
print("""** HELP **${argParser.usage}""");
|
||||
}
|
||||
|
||||
// usage显示所有help内容
|
||||
|
||||
final String name = argResults['name'];
|
||||
|
||||
print("Hello, $name!");
|
||||
}
|
105
tools/bin/trans2fw.dart
Normal file
105
tools/bin/trans2fw.dart
Normal file
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Created with Android Studio.
|
||||
* User: 一晟
|
||||
* Date: 2019/6/9
|
||||
* Time: 10:15 AM
|
||||
* email: zhu.yan@alibaba-inc.com
|
||||
* tartget:
|
||||
*/
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:dart_inquirer/dart_inquirer.dart';
|
||||
import 'package:console/console.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
const defaultPath = "";
|
||||
var projectPath = (Directory.current.uri).toString();
|
||||
|
||||
void main() async {
|
||||
qus_ans();
|
||||
}
|
||||
|
||||
void qus_ans() async {
|
||||
List<Question> questions = [
|
||||
// ListQuestion('select', format("{@green}从上面的选项中选择你的路径类型{@normal}"),
|
||||
// ['项目目录', '系统根目录']),
|
||||
InputQuestion('path', '请输入需要转换的文件目录(相对于项目目录):',
|
||||
skipIf: (Map ctx) => ctx["confirm"] == true),
|
||||
ConfirmQuestion('cover', '是否覆盖原有文件 ?', preferN: true),
|
||||
];
|
||||
Prompt prompt = Prompt(questions);
|
||||
Map answers = await prompt.execute();
|
||||
String filePath = answers["path"];
|
||||
String select = answers['select'];
|
||||
bool cover = answers['cover'];
|
||||
|
||||
///print(Seperator('='));
|
||||
print(answers);
|
||||
|
||||
// if (select == '项目目录') {
|
||||
// filePath = projectPath + filePath;
|
||||
// }
|
||||
|
||||
if (answers["path"] is String && (answers["path"] as String).isNotEmpty) {
|
||||
print("使用输入文件目录:${filePath}");
|
||||
printFilesAync(filePath, cover);
|
||||
} else if (!answers["confirm"] && answers["default"]) {
|
||||
print("使用默认文件目录:${defaultPath}");
|
||||
printFilesAync(defaultPath, cover);
|
||||
} else {
|
||||
print(format("{@red}放弃转换!{@normal}"));
|
||||
}
|
||||
}
|
||||
|
||||
void printFilesAync(String path, bool cover) async {
|
||||
try {
|
||||
var directory = new Directory(path);
|
||||
List<FileSystemEntity> files = directory.listSync();
|
||||
if (files.length == 0) {
|
||||
print(format("{@yellow}目录为空!{@normal}"));
|
||||
return;
|
||||
}
|
||||
for (var f in files) {
|
||||
var bool = FileSystemEntity.isFileSync(f.path);
|
||||
if (!bool) {
|
||||
//print('1::${bool}::${f.path}::${files.length}');
|
||||
await printFilesAync(f.path, cover);
|
||||
} else if (f.path.indexOf('.dart') != -1) {
|
||||
print('被转换的文件::${f.path}::${files.length}');
|
||||
var newPath = (f.path).replaceAll(new RegExp('.dart'), '-copy.dart');
|
||||
File newfile = new File(newPath);
|
||||
File oldfile = new File(f.path);
|
||||
File output = cover ? oldfile : newfile;
|
||||
|
||||
List<String> lines = oldfile.readAsLinesSync();
|
||||
|
||||
var result = '';
|
||||
lines.forEach((l) => {
|
||||
if (l.trim().indexOf('package:flutter/material.dart') >= 0)
|
||||
{result += "import 'package:flutter_web/material.dart';\n"}
|
||||
else if (l.trim().indexOf('package:flutter/widgets.dart') >= 0)
|
||||
{result += "import 'package:flutter_web/widgets.dart';\n"}
|
||||
else if (l.trim().indexOf('package:flutter/cupertino.dart') >= 0)
|
||||
{result += "import 'package:flutter_web/cupertino.dart';\n"}
|
||||
else if (l.trim().indexOf('package:flutter/services.dart') >= 0)
|
||||
{result += "import 'package:flutter_web/services.dart';\n"}
|
||||
else if (l.trim().indexOf('package:flutter/rendering.dart') >= 0)
|
||||
{result += "import 'package:flutter_web/rendering.dart';\n"}
|
||||
else if (l.trim().indexOf('dart:ui') >= 0)
|
||||
{result += "import 'package:flutter_web_ui/ui.dart';\n"}
|
||||
else
|
||||
{result += '${l}\n'}
|
||||
});
|
||||
output.writeAsStringSync(result);
|
||||
//newfile.writeAsStringSync(result);
|
||||
///await newfile.create();
|
||||
//await newfile.create();
|
||||
///file.createSync();
|
||||
///await oldfile.delete();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print(format("{@red}目录不存在!{@normal}") + ':${path}');
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
name: testCom
|
||||
name: trans2fw
|
||||
version: 0.0.1
|
||||
description: 自动化 flutter native 项目转化成 flutter-web 项目
|
||||
publish_to: none
|
||||
|
||||
dependencies:
|
||||
args: any
|
||||
dart_inquirer: any
|
||||
console: any
|
||||
path: any
|
||||
|
||||
executables:
|
||||
main:
|
||||
creatHtml:
|
||||
handl:
|
||||
build-fw:
|
@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>testTile</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Reference in New Issue
Block a user