From 49dd74babc132bddeb3c88b455ade8272e167fbe Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Mon, 18 Mar 2019 16:43:25 +0800 Subject: [PATCH 001/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0bottomnavigatorb?= =?UTF-8?q?ar=E5=86=85=E5=AE=B9,=E4=BF=AE=E6=94=B9=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=A4=96=E9=93=BE=E6=8E=A5=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../navigator/bottomnavigationbar/index.md | 41 ++++++- lib/components/widgetComp.dart | 2 +- lib/config/index.dart | 2 +- lib/page/mine/index.dart | 17 +-- .../navigator/bottomnavigationbar/demo.dart | 68 +++++++++++- .../demo_with_pageview.dart | 100 ++++++++++++++++++ .../navigator/bottomnavigationbar/index.dart | 4 +- .../navigator/floatingactionbutton/index.dart | 2 +- lib/widget/navigator/materialapp/index.dart | 2 +- .../navigator/popupmenubutton/index.dart | 2 +- lib/widget/navigator/scaffold/index.dart | 2 +- lib/widget/navigator/tabbar/index.dart | 2 +- lib/widget/navigator/tabbarview/index.dart | 2 +- locale/en.json | 5 +- locale/zh.json | 5 +- 15 files changed, 227 insertions(+), 29 deletions(-) create mode 100644 lib/widget/navigator/bottomnavigationbar/demo_with_pageview.dart diff --git a/docs/widget/navigator/bottomnavigationbar/index.md b/docs/widget/navigator/bottomnavigationbar/index.md index 83532f6..322d3a3 100644 --- a/docs/widget/navigator/bottomnavigationbar/index.md +++ b/docs/widget/navigator/bottomnavigationbar/index.md @@ -1 +1,40 @@ -## **文档完善中** \ No newline at end of file +## **BottomNavigationBar** +> Scaffold的属性,增加底部导航栏tab + +### 构造函数 +``` +BottomNavigationBar({ + Key key, + @required this.items, + this.onTap, + this.currentIndex = 0, + BottomNavigationBarType type, + this.fixedColor, + this.iconSize = 24.0, +}) +``` + +### 属性介绍 +- items: 数组类型,tab,通过BottomNavigationBarItem实现 +- onTap: 点击后回调函数,返回点击的数组下标,从0开始 +- currentIndex:当前激活的下标值 +- type: BottomNavigationBarType类型,默认fixed(固定位置大小,点击无动画效果),还有shifting(点击时有动画效果) + > fixed: 固定大小,无动画效果。 + > shifting: 带有动画效果,只有激活的标签带有主题颜色,可自行增加默认颜色 +- fixedColor: 图标与字体颜色,当type的值为BottomNavigationBarType.shifting时,级别比较低。 +- iconSize:double类型,图标大小。 + +### 高级用法 +- 可以定义一个变量,通过setState方法改变currentIndex的值。Scaffold中的body属性可以使用PageView,实现body页面切换带动导航栏切换,可导航栏触发body页面切换。 +- BottomNavigationBarItem:属性items中的组件 + ``` + BottomNavigationBarItem({ + @required this.icon, + this.title, + Widget activeIcon, + this.backgroundColor, + }) + ``` + > title: 定义标题 + > icon: 图标 + > activeIcon: 选中时图标 \ No newline at end of file diff --git a/lib/components/widgetComp.dart b/lib/components/widgetComp.dart index 8a06d9d..4283169 100644 --- a/lib/components/widgetComp.dart +++ b/lib/components/widgetComp.dart @@ -63,7 +63,7 @@ class IndexState extends State { void init() async { this._bodyList.length = 0; String mdText = await this.getMdFile(this.mdUrl); - if (mdText.length > 30) { + if (mdText.length > 30 || !this.model.config.state.isPro) { this ._bodyList .add(await MarkDownComp.Index(mdText)); diff --git a/lib/config/index.dart b/lib/config/index.dart index 350fcd5..08e9168 100644 --- a/lib/config/index.dart +++ b/lib/config/index.dart @@ -1,6 +1,6 @@ import 'development.dart' as Development; import 'production.dart' as Production; -const bool isPro = true; +const bool isPro = false; Object env = isPro ? Production.Config() : Development.Config(); diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index 636d2da..3b66005 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -3,12 +3,9 @@ import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/router/index.dart' show FluroRouter; class _IndexState extends State { - static String _version = ''; - @override void initState() { super.initState(); - _version = widget.model.config.state.version; } List _getList() { @@ -19,16 +16,9 @@ class _IndexState extends State { 'index': 0 }, { - 'name': AppLocalizations.$t('common.changeVersion') + ' ' + _version, - 'icon': 58919, // sync - 'index': 1, - 'show': !widget.model.config.state.isPro - }, - { - 'name': AppLocalizations.$t('common.changeEnvironment'), + 'name': widget.model.config.state.isPro ? AppLocalizations.$t('mine.loadLocal') : AppLocalizations.$t('mine.loadNetwork'), 'icon': 57539, // import_export 'index': 2, - 'show': !widget.model.config.state.isPro }, { 'name': AppLocalizations.$t('common.compProgress'), @@ -43,11 +33,6 @@ class _IndexState extends State { case 0: AppLocalizations.changeLanguage(); break; - case 1: - widget.model.dispatch('config', 'setVersion').then((resp) { - _version = widget.model.config.state.version; - }); - break; case 2: widget.model.dispatch('config', 'setEnv'); break; diff --git a/lib/widget/navigator/bottomnavigationbar/demo.dart b/lib/widget/navigator/bottomnavigationbar/demo.dart index 9d591a6..85683f8 100644 --- a/lib/widget/navigator/bottomnavigationbar/demo.dart +++ b/lib/widget/navigator/bottomnavigationbar/demo.dart @@ -11,6 +11,7 @@ class _IndexState extends State { super.initState(); } + int _index = 0; @override Widget build(BuildContext context) { return Scaffold( @@ -18,7 +19,72 @@ class _IndexState extends State { title: Text('BottomNavigationBar'), ), body: Center( - child: Text('更新中'), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Divider(), + Text( + '在Scaffold的bottomNavigationBar属性,增加BottomNavigationBar组件,实现底部导航栏。', + textAlign: TextAlign.center, + ), + Divider(), + Text( + '属性children通过BottomNavigationBarItem定义。', + textAlign: TextAlign.center, + ), + Divider(), + Text( + 'BottomNavigationBar能通过onTap增加点击事件', + textAlign: TextAlign.center, + ), + ], + ), + ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: _index, + onTap: (index) { + setState(() { + _index = index; + }); + }, + fixedColor: Colors.purple, + // type: BottomNavigationBarType.shifting, + items: [ + BottomNavigationBarItem( + title: Text( + '导航一', + style: TextStyle( + color: Colors.blue, + ), + ), + icon: Icon( + Icons.play_circle_filled, + color: Colors.blue, + ), + ), + BottomNavigationBarItem( + title: Text( + '导航二', + style: TextStyle( + color: Colors.blue, + ), + ), + icon: Icon( + Icons.pause_circle_filled, + color: Colors.blue, + ), + ), + BottomNavigationBarItem( + title: Text( + '导航三', + style: TextStyle( + color: Colors.blue, + ), + ), + icon: Icon(Icons.cloud_circle, + color: Colors.blue,), + ), + ], ), ); } diff --git a/lib/widget/navigator/bottomnavigationbar/demo_with_pageview.dart b/lib/widget/navigator/bottomnavigationbar/demo_with_pageview.dart new file mode 100644 index 0000000..108b07e --- /dev/null +++ b/lib/widget/navigator/bottomnavigationbar/demo_with_pageview.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; + +class Index extends StatefulWidget { + @override + State createState() => _IndexState(); +} + +class _IndexState extends State { + @override + void initState() { + super.initState(); + } + + int _index = 0; + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('BottomNavigationBar'), + ), + body: PageView( + onPageChanged: (index) { + setState(() { + _index = index; + }); + }, + children: [ + Center( + child: Column( + children: [ + Text('页面一'), + Text('左右滑动切换页面哦~~~'), + ], + ), + ), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('页面二'), + Text('左右滑动切换页面哦~~~'), + ], + ), + ), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text('页面三'), + Text('左右滑动切换页面哦~~~'), + ], + ), + ), + ]), + bottomNavigationBar: BottomNavigationBar( + currentIndex: _index, + type: BottomNavigationBarType.shifting, + onTap: (index) { + setState(() { + _index = index; + }); + }, + fixedColor: Colors.red, + iconSize: 28, + items: [ + BottomNavigationBarItem( + title: Text( + '导航一', + style: TextStyle(color: Colors.red), + ), + icon: Icon( + Icons.navigate_before, + color: Colors.redAccent, + ), + ), + BottomNavigationBarItem( + title: Text( + '导航二', + style: TextStyle(color: Colors.red), + ), + icon: Icon( + Icons.notifications_active, + color: Colors.redAccent, + ), + ), + BottomNavigationBarItem( + title: Text( + '导航三', + style: TextStyle(color: Colors.red), + ), + icon: Icon( + Icons.navigate_next, + color: Colors.redAccent, + ), + ), + ], + ), + ); + } +} diff --git a/lib/widget/navigator/bottomnavigationbar/index.dart b/lib/widget/navigator/bottomnavigationbar/index.dart index 5c8997f..d939240 100644 --- a/lib/widget/navigator/bottomnavigationbar/index.dart +++ b/lib/widget/navigator/bottomnavigationbar/index.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; import 'demo.dart' as Demo; +import 'demo_with_pageview.dart' as DemoWithPageView; class Index extends StatefulWidget { static String title = 'BottomNavigationBar'; static String mdUrl = 'docs/widget/navigator/bottomnavigationbar/index.md'; - static String originCodeUrl = 'https://docs.flutter.io/flutter/widgets/BottomNavigationBar-class.html'; + static String originCodeUrl = 'https://docs.flutter.io/flutter/material/BottomNavigationBar-class.html'; @override _IndexState createState() => new _IndexState(); @@ -20,6 +21,7 @@ class _IndexState extends State { mdUrl: Index.mdUrl, demoChild: [ Demo.Index(), + DemoWithPageView.Index(), ], ); } diff --git a/lib/widget/navigator/floatingactionbutton/index.dart b/lib/widget/navigator/floatingactionbutton/index.dart index 8fef222..2812f52 100644 --- a/lib/widget/navigator/floatingactionbutton/index.dart +++ b/lib/widget/navigator/floatingactionbutton/index.dart @@ -5,7 +5,7 @@ import 'demo.dart' as Demo; class Index extends StatefulWidget { static String title = 'FloatingActionButton'; static String mdUrl = 'docs/widget/navigator/floatingactionbutton/index.md'; - static String originCodeUrl = 'https://docs.flutter.io/flutter/widgets/FloatingActionButton-class.html'; + static String originCodeUrl = 'https://docs.flutter.io/flutter/material/FloatingActionButton-class.html'; @override _IndexState createState() => new _IndexState(); diff --git a/lib/widget/navigator/materialapp/index.dart b/lib/widget/navigator/materialapp/index.dart index 4882737..3510d27 100644 --- a/lib/widget/navigator/materialapp/index.dart +++ b/lib/widget/navigator/materialapp/index.dart @@ -5,7 +5,7 @@ import 'demo.dart' as Demo; class Index extends StatefulWidget { static String title = 'MaterialApp'; static String mdUrl = 'docs/widget/navigator/materialapp/index.md'; - static String originCodeUrl = 'https://docs.flutter.io/flutter/widgets/MaterialApp-class.html'; + static String originCodeUrl = 'https://docs.flutter.io/flutter/material/MaterialApp-class.html'; @override _IndexState createState() => new _IndexState(); diff --git a/lib/widget/navigator/popupmenubutton/index.dart b/lib/widget/navigator/popupmenubutton/index.dart index a58b009..a4db5a5 100644 --- a/lib/widget/navigator/popupmenubutton/index.dart +++ b/lib/widget/navigator/popupmenubutton/index.dart @@ -5,7 +5,7 @@ import 'demo.dart' as Demo; class Index extends StatefulWidget { static String title = 'PopupMenuButton'; static String mdUrl = 'docs/widget/navigator/popupmenubutton/index.md'; - static String originCodeUrl = 'https://docs.flutter.io/flutter/widgets/PopupMenuButton-class.html'; + static String originCodeUrl = 'https://docs.flutter.io/flutter/material/PopupMenuButton-class.html'; @override _IndexState createState() => new _IndexState(); diff --git a/lib/widget/navigator/scaffold/index.dart b/lib/widget/navigator/scaffold/index.dart index 6fb2555..b103bc2 100644 --- a/lib/widget/navigator/scaffold/index.dart +++ b/lib/widget/navigator/scaffold/index.dart @@ -4,8 +4,8 @@ import 'demo.dart' as Demo; class Index extends StatefulWidget { static String title = 'Scaffold'; - static String originCodeUrl = 'https://docs.flutter.io/flutter/material/Scaffold-class.html'; static String mdUrl = 'docs/widget/navigator/scaffold/index.md'; + static String originCodeUrl = 'https://docs.flutter.io/flutter/material/Scaffold-class.html'; @override _IndexState createState() => _IndexState(); diff --git a/lib/widget/navigator/tabbar/index.dart b/lib/widget/navigator/tabbar/index.dart index 4f5bb68..2440c6e 100644 --- a/lib/widget/navigator/tabbar/index.dart +++ b/lib/widget/navigator/tabbar/index.dart @@ -5,7 +5,7 @@ import 'demo.dart' as Demo; class Index extends StatefulWidget { static String title = 'TabBar'; static String mdUrl = 'docs/widget/navigator/tabbar/index.md'; - static String originCodeUrl = 'https://docs.flutter.io/flutter/widgets/TabBar-class.html'; + static String originCodeUrl = 'https://docs.flutter.io/flutter/material/TabBar-class.html'; @override _IndexState createState() => new _IndexState(); diff --git a/lib/widget/navigator/tabbarview/index.dart b/lib/widget/navigator/tabbarview/index.dart index 9060eb2..5d680ec 100644 --- a/lib/widget/navigator/tabbarview/index.dart +++ b/lib/widget/navigator/tabbarview/index.dart @@ -5,7 +5,7 @@ import 'demo.dart' as Demo; class Index extends StatefulWidget { static String title = 'TabBarView'; static String mdUrl = 'docs/widget/navigator/tabbarview/index.md'; - static String originCodeUrl = 'https://docs.flutter.io/flutter/widgets/TabBarView-class.html'; + static String originCodeUrl = 'https://docs.flutter.io/flutter/material/TabBarView-class.html'; @override _IndexState createState() => new _IndexState(); diff --git a/locale/en.json b/locale/en.json index b2134ea..b09f79a 100644 --- a/locale/en.json +++ b/locale/en.json @@ -9,8 +9,11 @@ "common" : { "changeLanguage": "显示中文", "changeVersion": "checkVersion", - "changeEnvironment": "changeEnvironment", "compProgress": "Components Progress" }, + "mine": { + "loadNetwork": "Load Network Document Resources", + "loadLocal": "Load Local Document Resources" + }, "loading": "Loading" } \ No newline at end of file diff --git a/locale/zh.json b/locale/zh.json index 5f08471..c644e29 100644 --- a/locale/zh.json +++ b/locale/zh.json @@ -9,8 +9,11 @@ "common" : { "changeLanguage": "Switch to English", "changeVersion": "更新版本", - "changeEnvironment": "切换环境", "compProgress": "组件进度" }, + "mine": { + "loadNetwork": "加载网络文档资源", + "loadLocal": "加载本地文档资源" + }, "loading": "加载中" } \ No newline at end of file From 1a325d604192116a72e3ec3b0b9a40d2ca7097f8 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Mon, 18 Mar 2019 19:26:27 +0800 Subject: [PATCH 002/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0md=E5=AF=B9?= =?UTF-8?q?=E5=BA=94=E7=9A=84=E7=BB=84=E4=BB=B6=E5=86=85=E5=AE=B9=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0floatingactionbutton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../navigator/floatingactionbutton/index.md | 2 +- .../navigator/floatingactionbutton/demo.dart | 28 ++++++++++- readme.md | 46 +++++++++---------- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/docs/widget/navigator/floatingactionbutton/index.md b/docs/widget/navigator/floatingactionbutton/index.md index 83532f6..ae18bd0 100644 --- a/docs/widget/navigator/floatingactionbutton/index.md +++ b/docs/widget/navigator/floatingactionbutton/index.md @@ -1 +1 @@ -## **文档完善中** \ No newline at end of file +## **FloatingActionButton** \ No newline at end of file diff --git a/lib/widget/navigator/floatingactionbutton/demo.dart b/lib/widget/navigator/floatingactionbutton/demo.dart index bb81fb3..29b249b 100644 --- a/lib/widget/navigator/floatingactionbutton/demo.dart +++ b/lib/widget/navigator/floatingactionbutton/demo.dart @@ -6,6 +6,7 @@ class Index extends StatefulWidget { } class _IndexState extends State { + int count = 0; @override void initState() { super.initState(); @@ -16,9 +17,34 @@ class _IndexState extends State { return Scaffold( appBar: AppBar( title: Text('FloatingActionButton'), + automaticallyImplyLeading: false, ), body: Center( - child: Text('更新中'), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Click FloatingActionButton to add Count', + style: TextStyle(fontSize: 16), + ), + Text( + 'count: $count', + style: TextStyle(fontSize: 24), + ) + ], + ), + ), + floatingActionButton: FloatingActionButton( + backgroundColor: Colors.red, + child: Icon( + Icons.notifications_active, + color: Colors.white, + ), + onPressed: () { + setState(() { + count++; + }); + }, ), ); } diff --git a/readme.md b/readme.md index 45ad2df..6cd3c9c 100644 --- a/readme.md +++ b/readme.md @@ -58,7 +58,7 @@ Flutter UI ├─utils 项目工具类 └─widget 项目组件库 ├─animate - │ ├─animatedbuilder 【✔️ v1.0】 + │ ├─animatedbuilder 【✔️ v1.0】 │ ├─animatedcontainer │ ├─animatedcrossfade │ ├─animateddefaulttextstyle @@ -89,8 +89,8 @@ Flutter UI │ ├─assetbundle │ ├─buttonbar │ ├─chip - │ ├─container 【✔️ v1.0】 - │ ├─divider 【✔️ v1.0】 + │ ├─container 【✔️ v1.0】 + │ ├─divider 【✔️ v1.0】 │ ├─flatbutton 【✔️ v1.0】 │ ├─icon 【✔️ v1.0】 │ ├─iconbutton @@ -105,16 +105,16 @@ Flutter UI ├─form │ ├─checkbox 【✔️ v1.0】 │ ├─checkboxlisttile 【✔️ v1.0】 - │ ├─slider 【✔️ v1.0】 - │ ├─switch 【✔️ v1.0】 - │ ├─switchListTile 【✔️ v1.0】 - │ ├─daypicker 【✔️ v1.0】 - │ ├─radio 【✔️ v1.0】 - │ ├─radioListTile 【✔️ v1.0】 + │ ├─slider 【✔️ v1.0】 + │ ├─switch 【✔️ v1.0】 + │ ├─switchListTile 【✔️ v1.0】 + │ ├─daypicker 【✔️ v1.0】 + │ ├─radio 【✔️ v1.0】 + │ ├─radioListTile 【✔️ v1.0】 │ ├─form │ ├─formfield │ ├─rawkeyboard - │ ├─textfield 【✔️ v1.0】 + │ ├─textfield 【✔️ v1.0】 │ └─textinput ├─gestures │ ├─absorbpointer @@ -125,9 +125,9 @@ Flutter UI │ └─longpressdraggable 【✔️ v1.0】 ├─navigator │ ├─appbar 【✔️ v1.0】 - │ ├─bottomnavigationbar + │ ├─bottomnavigationbar 【✔️ v1.0】 │ ├─drawer 【✔️ v1.0】 - │ ├─floatingactionbutton + │ ├─floatingactionbutton 【✔️ v1.0】 │ ├─materialapp │ ├─navigator │ ├─popupmenubutton @@ -136,21 +136,21 @@ Flutter UI │ ├─tabbarview │ └─widgetsapp ├─regular - │ ├─align 【✔️ v1.0】 - │ ├─aspectratio 【✔️ v1.0】 + │ ├─align 【✔️ v1.0】 + │ ├─aspectratio 【✔️ v1.0】 │ ├─center 【✔️ v1.0】 │ ├─column 【✔️ v1.0】 │ ├─constrainedbox 【✔️ v1.0】 - │ ├─container 【✔️ v1.0】 - │ ├─fittedbox 【✔️ v1.0】 + │ ├─container 【✔️ v1.0】 + │ ├─fittedbox 【✔️ v1.0】 │ ├─flow 【✔️ v1.0】 - │ ├─layoutbuilder 【✔️ v1.0】 + │ ├─layoutbuilder 【✔️ v1.0】 │ ├─listbody 【✔️ v1.0】 │ ├─listview 【✔️ v1.0】 - │ ├─padding 【✔️ v1.0】 - │ ├─row 【✔️ v1.0】 - │ ├─stack 【✔️ v1.0】 - │ ├─table 【✔️ v1.0】 + │ ├─padding 【✔️ v1.0】 + │ ├─row 【✔️ v1.0】 + │ ├─stack 【✔️ v1.0】 + │ ├─table 【✔️ v1.0】 │ └─wrap 【✔️ v1.0】 ├─scrollview │ ├─customscrollview 【✔️ v1.0】 @@ -158,9 +158,9 @@ Flutter UI │ ├─listview 【✔️ v1.0】 │ ├─nestedscrollview 【✔️ v1.0】 │ ├─scrollable 【✔️ v1.0】 - │ ├─scrollbar 【✔️ v1.0】 + │ ├─scrollbar 【✔️ v1.0】 │ ├─scrollcontroller 【✔️ v1.0】 - │ └─singlechildscrollview 【✔️ v1.0】 + │ └─singlechildscrollview 【✔️ v1.0】 └─vision ├─backdropfilter ├─clipoval From ccf245c0c69925bd7e17adead2634c06c4f83cc4 Mon Sep 17 00:00:00 2001 From: wanwu <609780590@qq.com> Date: Mon, 18 Mar 2019 20:15:01 +0800 Subject: [PATCH 003/100] fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改图片链接 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 45ad2df..d017b9b 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ # Flutter UI -![https://raw.githubusercontent.com/efoxTeam/flutter-ui/dev-ken/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png](https://raw.githubusercontent.com/efoxTeam/flutter-ui/dev-ken/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png) +![https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png](https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png) > flutter ui 开发者社区 提供各种flutter相关开发教程 与 demo From d0231f1c2a108d511f24fd25fb2c8e87a54ed948 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Mon, 18 Mar 2019 21:48:31 +0800 Subject: [PATCH 004/100] =?UTF-8?q?refactor:=E4=BC=98=E5=8C=96floatingacti?= =?UTF-8?q?onbutton=E5=86=85=E5=AE=B9=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../navigator/floatingactionbutton/index.md | 36 ++++++++++++++++++- .../navigator/floatingactionbutton/demo.dart | 8 ++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/docs/widget/navigator/floatingactionbutton/index.md b/docs/widget/navigator/floatingactionbutton/index.md index ae18bd0..cb07549 100644 --- a/docs/widget/navigator/floatingactionbutton/index.md +++ b/docs/widget/navigator/floatingactionbutton/index.md @@ -1 +1,35 @@ -## **FloatingActionButton** \ No newline at end of file +## **FloatingActionButton** +> 在Scaffold中属性floatingActionButton实现FloatingActionButton,该组件是个圆形的浮层按钮,一般情况下child可用icon进行绘画。 + +### 构造函数 +``` +FloatingActionButton({ + Key key, + this.child, + this.tooltip, + this.foregroundColor, + this.backgroundColor, + this.heroTag = const _DefaultHeroTag(), + this.elevation = 6.0, + this.highlightElevation = 12.0, + double disabledElevation, + @required this.onPressed, + this.mini = false, + this.shape = const CircleBorder(), + this.clipBehavior = Clip.none, + this.materialTapTargetSize, + this.isExtended = false, +}) +``` + +### 属性介绍 +- onPressed: 必填属性,函数调用,点击按钮后回调事件 +- child: 组件子组件内容,可通过Icon简单的增加图标 +- elevation:图标下阴影大小 +- highlightElevation: 点击后阴影扩散范围 +- tooltip:长按提示 +- foregroundColor:前景色,如图标、文字的颜色 +- backgroundColor:背景填充色 +- mini: 默认false,true时为迷你按钮 + + diff --git a/lib/widget/navigator/floatingactionbutton/demo.dart b/lib/widget/navigator/floatingactionbutton/demo.dart index 29b249b..4f867dd 100644 --- a/lib/widget/navigator/floatingactionbutton/demo.dart +++ b/lib/widget/navigator/floatingactionbutton/demo.dart @@ -35,7 +35,13 @@ class _IndexState extends State { ), ), floatingActionButton: FloatingActionButton( - backgroundColor: Colors.red, + clipBehavior: Clip.antiAliasWithSaveLayer, + tooltip: '长按后的提示', + foregroundColor:Colors.blue, // 该颜色被Icon覆盖 + backgroundColor: Colors.red, // 背景填充色 + elevation: 10, // 下阴影大小 + highlightElevation: 50, // 阴影扩散范围 + mini: true, child: Icon( Icons.notifications_active, color: Colors.white, From f02b0bc29150300561cd4284a3df2c44d07f7b93 Mon Sep 17 00:00:00 2001 From: KenZR Date: Mon, 18 Mar 2019 22:20:03 +0800 Subject: [PATCH 005/100] =?UTF-8?q?fix:=E4=BC=98=E5=8C=96=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index c87deb4..557e220 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ # Flutter UI -![https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png](https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png) +![android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png](android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png) > flutter ui 开发者社区 提供各种flutter相关开发教程 与 demo @@ -13,7 +13,7 @@ [历史版本](https://github.com/efoxTeam/flutter-ui/releases) ## 开发者如何参与完善控件 - + 欢迎有兴趣的小伙伴QQ扫描以上二维码参与进来,一同完善组件 同时也可以把相关问题通过[issues](https://github.com/efoxTeam/flutter-ui/issues)方式与我们联系 From 0fa3b5cbcc69dbc40a7fb3ea171c15f15604e82d Mon Sep 17 00:00:00 2001 From: KenZR Date: Mon, 18 Mar 2019 23:25:24 +0800 Subject: [PATCH 006/100] =?UTF-8?q?fix:=E5=AE=8C=E6=88=90ios=20firebase?= =?UTF-8?q?=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 78 ++++++++++++++++--- .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++ .../contents.xcworkspacedata | 3 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++ .../xcshareddata/WorkspaceSettings.xcsettings | 8 ++ ios/Runner/AppDelegate.m | 5 ++ ios/Runner/GoogleService-Info.plist | 40 ++++++++++ ios/Runner/Info.plist | 2 + 8 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner/GoogleService-Info.plist diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 5ba8d87..7458b08 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -20,6 +19,8 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + BA61DFEF7B89A2B0064AE279 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D7BE9010C4F7FF033BA299B7 /* libPods-Runner.a */; }; + BEBBA522223FEA6600583D52 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = BEBBA521223FEA6600583D52 /* GoogleService-Info.plist */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -40,7 +41,6 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -55,6 +55,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BEBBA521223FEA6600583D52 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + D7BE9010C4F7FF033BA299B7 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -64,16 +66,31 @@ files = ( 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + BA61DFEF7B89A2B0064AE279 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 607E308685FBC1196333D679 /* Frameworks */ = { + isa = PBXGroup; + children = ( + D7BE9010C4F7FF033BA299B7 /* libPods-Runner.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 8B5D251E963699100C93BD5E /* Pods */ = { + isa = PBXGroup; + children = ( + ); + name = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEBA1CF902C7004384FC /* Flutter.framework */, @@ -90,7 +107,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + 8B5D251E963699100C93BD5E /* Pods */, + 607E308685FBC1196333D679 /* Frameworks */, ); sourceTree = ""; }; @@ -105,6 +123,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + BEBBA521223FEA6600583D52 /* GoogleService-Info.plist */, 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 97C146FA1CF9000F007C117D /* Main.storyboard */, @@ -133,12 +152,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 9C2B8B9B587696DA01082626 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + EB6D2D2E068567EE260A8427 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -160,6 +181,7 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = 5K9P57YFSL; }; }; }; @@ -190,8 +212,8 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + BEBBA522223FEA6600583D52 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -226,6 +248,42 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + 9C2B8B9B587696DA01082626 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + EB6D2D2E068567EE260A8427 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -315,7 +373,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = 5K9P57YFSL; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -327,7 +385,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.efoxFlutter; + PRODUCT_BUNDLE_IDENTIFIER = com.flutter.beer; PRODUCT_NAME = "$(TARGET_NAME)"; VERSIONING_SYSTEM = "apple-generic"; }; @@ -441,6 +499,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 5K9P57YFSL; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -452,7 +511,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.efoxFlutter; + PRODUCT_BUNDLE_IDENTIFIER = com.flutter.beer; PRODUCT_NAME = "$(TARGET_NAME)"; VERSIONING_SYSTEM = "apple-generic"; }; @@ -464,6 +523,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 5K9P57YFSL; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -475,7 +535,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.efoxFlutter; + PRODUCT_BUNDLE_IDENTIFIER = com.flutter.beer; PRODUCT_NAME = "$(TARGET_NAME)"; VERSIONING_SYSTEM = "apple-generic"; }; diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..949b678 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + BuildSystemType + Original + + diff --git a/ios/Runner/AppDelegate.m b/ios/Runner/AppDelegate.m index 59a72e9..c185aa5 100644 --- a/ios/Runner/AppDelegate.m +++ b/ios/Runner/AppDelegate.m @@ -1,10 +1,15 @@ #include "AppDelegate.h" #include "GeneratedPluginRegistrant.h" +@import Firebase;//增加 firebase 支持 + @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + + [FIRApp configure];//增加 firebase 支持 + [GeneratedPluginRegistrant registerWithRegistry:self]; // Override point for customization after application launch. return [super application:application didFinishLaunchingWithOptions:launchOptions]; diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist new file mode 100644 index 0000000..bf356a4 --- /dev/null +++ b/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,40 @@ + + + + + AD_UNIT_ID_FOR_BANNER_TEST + ca-app-pub-3940256099942544/2934735716 + AD_UNIT_ID_FOR_INTERSTITIAL_TEST + ca-app-pub-3940256099942544/4411468910 + CLIENT_ID + 27281760426-agmn2b0f4dr988t098rm5259v7i7cbkf.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.27281760426-agmn2b0f4dr988t098rm5259v7i7cbkf + API_KEY + AIzaSyBUOZNJqMvIF_QEJGHXu0ponVCGPo5YarI + GCM_SENDER_ID + 27281760426 + PLIST_VERSION + 1 + BUNDLE_ID + com.flutter.beer + PROJECT_ID + efox-flutter + STORAGE_BUCKET + efox-flutter.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:27281760426:ios:51548b1d738060a2 + DATABASE_URL + https://efox-flutter.firebaseio.com + + \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 2f03a2f..a5b0ab9 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -4,6 +4,8 @@ CFBundleDevelopmentRegion en + CFBundleDisplayName + flutter ui CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier From 9ead94be3ae0e5c700620832cb9a6d839d1b1e1b Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 19 Mar 2019 13:14:15 +0800 Subject: [PATCH 007/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0Android=20Crashl?= =?UTF-8?q?ytics=20SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 8 ++++++-- android/build.gradle | 12 +++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 2ab784b..7108555 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -82,7 +82,11 @@ dependencies { androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' //firebase - implementation 'com.google.firebase:firebase-core:16.0.7' + implementation 'com.google.firebase:firebase-core:16.0.8' + //Crashlytics SDK + implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9' } //firebase -apply plugin: 'com.google.gms.google-services' \ No newline at end of file +apply plugin: 'com.google.gms.google-services' +//Crashlytics SDK +apply plugin: 'io.fabric' \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 131b586..eb44602 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,12 +2,18 @@ buildscript { repositories { google() jcenter() + //Crashlytics SDK + maven { + url 'https://maven.fabric.io/public' + } } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.2' //firebase classpath 'com.google.gms:google-services:4.2.0' + //Crashlytics SDK + classpath 'io.fabric.tools:gradle:1.26.1' } } @@ -15,6 +21,10 @@ allprojects { repositories { google() jcenter() + //Crashlytics SDK + maven { + url 'https://maven.google.com/' + } } } From 5bb7581566d46931386862cd2d26a72028da7051 Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 19 Mar 2019 17:25:41 +0800 Subject: [PATCH 008/100] =?UTF-8?q?refactor:=E4=BC=98=E5=8C=96=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/config/theme.dart | 5 +- lib/page/component/tabs.dart | 186 +++++++++++++++++++++++++++++++++++ lib/page/home.dart | 6 +- lib/page/mine/index.dart | 34 ++++--- 4 files changed, 215 insertions(+), 16 deletions(-) create mode 100644 lib/page/component/tabs.dart diff --git a/lib/config/theme.dart b/lib/config/theme.dart index 4746a6c..967ab66 100644 --- a/lib/config/theme.dart +++ b/lib/config/theme.dart @@ -6,6 +6,7 @@ class AppTheme { static int thirdColor = 0xFFFAFAFA; static int greyColor = 0x8A000000; static int blackColor = 0xFF000000; + static int lineColor = 0xFFEEEEEE; static ThemeData themData = ThemeData( textTheme: TextTheme( body1: TextStyle( @@ -13,7 +14,7 @@ class AppTheme { // fontWeight: FontWeight.bold, ), ), - platform: TargetPlatform.iOS, + //platform: TargetPlatform.iOS, iconTheme: IconThemeData( size: 32, color: Color(thirdColor), @@ -25,6 +26,6 @@ class AppTheme { ), accentColor: Colors.grey, // 选中颜色 primaryColor: Color(mainColor), // appbar背景 - scaffoldBackgroundColor: Color(thirdColor), // 整体的scaffold背景颜色 + scaffoldBackgroundColor: Color(secondColor), // 整体的scaffold背景颜色 ); } diff --git a/lib/page/component/tabs.dart b/lib/page/component/tabs.dart new file mode 100644 index 0000000..e59b3b3 --- /dev/null +++ b/lib/page/component/tabs.dart @@ -0,0 +1,186 @@ +import 'package:flutter/material.dart'; +import 'package:efox_flutter/store/models/main_state_model.dart' + show MainStateModel; +import 'package:efox_flutter/config/theme.dart' show AppTheme; +import 'package:efox_flutter/widget/index.dart' as WidgetRoot; +import 'package:efox_flutter/router/index.dart' show FluroRouter; +import 'package:efox_flutter/lang/index.dart' show AppLocalizations; +import 'package:efox_flutter/components/headerComp.dart' as Header; + +class Index extends StatefulWidget { + final MainStateModel model; + Index({Key key, this.model}) : super(key: key); + @override + _IndexState createState() => new _IndexState(); +} + +class _IndexState extends State + with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { + List _mapList = WidgetRoot.getAllWidgets(); + int _currentIndex = -1; + TabController _tabController; + + @override + bool get wantKeepAlive => true; + + @override + initState() { + super.initState(); + _tabController = new TabController(vsync: this, length: _mapList.length); + _tabController.addListener(() { + if (_currentIndex != _tabController.index) { + _currentIndex = _tabController.index; + } + }); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + final GlobalKey _scaffoldKey = new GlobalKey(); + + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + /* appBar: AppBar( + title: Header.Index( + AppLocalizations.$t('nav_title_0'), + ), + actions: appBarActions(), + ), */ + appBar: PreferredSize( + preferredSize: Size.fromHeight(kToolbarHeight), + child: Container( + color: Color(AppTheme.mainColor), + child: SafeArea( + child: this._TabBar(), + ), + ), + ), + body: Column( + children: [ + //this._TabBar(), + this._TabView() + ], + )); + } + + Widget _TabBar() { + return TabBar( + controller: _tabController, + isScrollable: true, + tabs: _mapList.map((v) { + return new Tab( + text: v.typeName, + /* icon: Icon( + IconData( + v.code, + fontFamily: 'MaterialIcons', + matchTextDirection: true, + ), + ), */ + ); + }).toList()); + } + + Widget _TabView() { + return Expanded( + child: new TabBarView( + controller: _tabController, + children: List.generate(_mapList.length, (index) { + return this.Grids(_mapList[index], index); + })), + ); + } + + Widget Grids(widgetsItem, index) { + String nameSpaces = widgetsItem.nameSpaces; + List _tmpWidgetList = widgetsItem.widgetList; + + return Container( + child: GridView.count( + childAspectRatio: 1.3, + padding: EdgeInsets.fromLTRB(4, 0, 4, 0), + shrinkWrap: true, + physics: ScrollPhysics(), + crossAxisCount: 3, + children: List.generate( + _tmpWidgetList.length, + (index) { + return Container( + decoration: BoxDecoration( + border: + Border.all(color: Color(AppTheme.lineColor), width: 0.5)), + child: FlatButton( + color: Color(AppTheme.secondColor), + splashColor: Color(AppTheme.mainColor), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Icon( + IconData( + _tmpWidgetList[index].code, + fontFamily: 'MaterialIcons', + matchTextDirection: true, + ), + color: Color(AppTheme.mainColor), + size: 32, + ), + Text( + '${_tmpWidgetList[index].title}', + //overflow: TextOverflow.ellipsis, + style: + TextStyle(fontSize: 14, fontWeight: FontWeight.w300), + ) + ], + ), + onPressed: () { + FluroRouter.router.navigateTo( + context, + nameSpaces + _tmpWidgetList[index].title, + ); + }, + ), + ); + }, + ), + ), + ); + } + + List appBarActions() { + return [ + PopupMenuButton( + icon: Icon( + Icons.more_vert, + ), + onSelected: (local) { + AppLocalizations.changeLanguage(Locale(local)); + print('local=$local'); + }, + itemBuilder: (context) => [ + PopupMenuItem( + child: Row( + children: [ + Text('中文'), + ], + ), + value: 'zh', + ), + PopupMenuItem( + child: Row( + children: [ + Text('english'), + ], + ), + value: 'en', + ), + ], + ), + ]; + } +} diff --git a/lib/page/home.dart b/lib/page/home.dart index ae6a0f6..54f6c09 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -3,7 +3,8 @@ import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/store/index.dart' show Store; import 'package:efox_flutter/controller/index.dart' as Controller; -import 'component/index.dart' as TabIndex; +//import 'component/index.dart' as TabIndex; +import 'component/tabs.dart' as TabIndex; import 'mine/index.dart' as MyIndex; class Index extends StatefulWidget { @@ -35,7 +36,8 @@ class _IndexState extends State { title: Text(AppLocalizations.$t('title_component')), icon: Icon(Icons.dashboard)), BottomNavigationBarItem( - title: Text(AppLocalizations.$t('title_my')), icon: Icon(Icons.person_outline)), + title: Text(AppLocalizations.$t('title_my')), + icon: Icon(Icons.person_outline)), ], type: BottomNavigationBarType.fixed, currentIndex: _currentIndex, diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index 3b66005..b1266eb 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/router/index.dart' show FluroRouter; +import 'package:efox_flutter/config/theme.dart' show AppTheme; class _IndexState extends State { @override @@ -16,7 +17,9 @@ class _IndexState extends State { 'index': 0 }, { - 'name': widget.model.config.state.isPro ? AppLocalizations.$t('mine.loadLocal') : AppLocalizations.$t('mine.loadNetwork'), + 'name': widget.model.config.state.isPro + ? AppLocalizations.$t('mine.loadLocal') + : AppLocalizations.$t('mine.loadNetwork'), 'icon': 57539, // import_export 'index': 2, }, @@ -86,18 +89,25 @@ class _IndexState extends State { (context, index) { dynamic item = list[index]; if (item['show'] ?? true) { - return ListTile( - onTap: () { - this.actionsEvent(item['index']); - }, - leading: Icon( - IconData( - item['icon'], - fontFamily: 'MaterialIcons', - matchTextDirection: true, + return Column( + children: [ + ListTile( + onTap: () { + this.actionsEvent(item['index']); + }, + leading: Icon( + IconData( + item['icon'], + fontFamily: 'MaterialIcons', + matchTextDirection: true, + ), + ), + title: Text('${item['name']}'), ), - ), - title: Text('${item['name']}'), + Divider( + color: Color(AppTheme.lineColor), + ) + ], ); } else { return Container(); From 60dfffd10eba1b0cd2c72af37146e5a39b786e40 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Tue, 19 Mar 2019 17:29:06 +0800 Subject: [PATCH 009/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0pageview?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/widget/navigator/pageview/index.md | 27 ++++ .../widget/navigator/popupmenubutton/index.md | 35 ++++- .../demo_with_pageview.dart | 67 +++++---- lib/widget/navigator/index.dart | 6 + lib/widget/navigator/pageview/demo.dart | 135 ++++++++++++++++++ lib/widget/navigator/pageview/index.dart | 26 ++++ .../navigator/popupmenubutton/demo.dart | 83 ++++++++++- pubspec.yaml | 1 + 8 files changed, 347 insertions(+), 33 deletions(-) create mode 100644 docs/widget/navigator/pageview/index.md create mode 100644 lib/widget/navigator/pageview/demo.dart create mode 100644 lib/widget/navigator/pageview/index.dart diff --git a/docs/widget/navigator/pageview/index.md b/docs/widget/navigator/pageview/index.md new file mode 100644 index 0000000..bb90c0b --- /dev/null +++ b/docs/widget/navigator/pageview/index.md @@ -0,0 +1,27 @@ +## **PageView** +> 创建一个整屏滚动的滚动列表,逐页滚动 + + +### 构造函数 +``` +PageView({ + Key key, + Axis scrollDirection: Axis.horizontal, + bool reverse: false, + PageController controller, + ScrollPhysics physics, + bool pageSnapping: true, + ValueChanged onPageChanged, + List children: const [], + DragStartBehavior dragStartBehavior: DragStartBehavior.down +}) +``` + +### 属性介绍 +- children:页面列表,每个子元素对应一个当前页。 +- scrollDirection: Axis.horizontal/Axis.vertical, 默认是水平方向,可选择垂直方向滚动 +- reverse: 滚动方向取反操作 +- controller: 操作页面滚动行为类,可以通过PageController实例后的对象进行指定页面跳转,可携带特效跳转等。PageController.jumpToPage(index) +- physics: 滚动属性,可参考滚动类别中的gridview等相近属性介绍 +- pageSnapping: 默认true,切换时,自动逐页跳转。当自定义滚动行为时,可设置为false,禁止页面捕获。 +- onPageChanged: 页面切换时,回调函数,返回页面下标值 diff --git a/docs/widget/navigator/popupmenubutton/index.md b/docs/widget/navigator/popupmenubutton/index.md index 83532f6..7a448e5 100644 --- a/docs/widget/navigator/popupmenubutton/index.md +++ b/docs/widget/navigator/popupmenubutton/index.md @@ -1 +1,34 @@ -## **文档完善中** \ No newline at end of file +## **PopupMenuButton** + +### 构造函数 +``` +PopupMenuButton({ + Key key, + @required PopupMenuItemBuilder itemBuilder, + T initialValue, + PopupMenuItemSelected onSelected, + PopupMenuCanceled onCanceled, + String tooltip, + double elevation: 8.0, + EdgeInsetsGeometry padding: const EdgeInsets.all(8.0), + Widget child, + Icon icon, + Offset offset: Offset.zero +}) +``` + +### 属性介绍 +- itemBuilder: (_) => { return [PopupMenuItem(), PopupMenuItem(),]} + > PopupMenuItem: 菜单子组件 + ``` + PopupMenuItem({ + Key key, + T value, + bool enabled: true, + double height: _kMenuItemHeight, + @required Widget child + }) + ``` +- onSelected: 选中后返回PopupMenuItem中value的值 +- child: 有默认图标,可以修改为其它显示内容。 + diff --git a/lib/widget/navigator/bottomnavigationbar/demo_with_pageview.dart b/lib/widget/navigator/bottomnavigationbar/demo_with_pageview.dart index 108b07e..2281d63 100644 --- a/lib/widget/navigator/bottomnavigationbar/demo_with_pageview.dart +++ b/lib/widget/navigator/bottomnavigationbar/demo_with_pageview.dart @@ -6,12 +6,14 @@ class Index extends StatefulWidget { } class _IndexState extends State { + int _index = 0; + PageController _pageController; @override void initState() { super.initState(); + _pageController = PageController(); } - int _index = 0; @override Widget build(BuildContext context) { return Scaffold( @@ -19,39 +21,41 @@ class _IndexState extends State { title: Text('BottomNavigationBar'), ), body: PageView( - onPageChanged: (index) { - setState(() { - _index = index; - }); - }, - children: [ - Center( - child: Column( - children: [ - Text('页面一'), - Text('左右滑动切换页面哦~~~'), - ], - ), + controller: _pageController, + onPageChanged: (index) { + setState(() { + _index = index; + }); + }, + children: [ + Center( + child: Column( + children: [ + Text('页面一'), + Text('左右滑动切换页面哦~~~'), + ], ), - Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('页面二'), - Text('左右滑动切换页面哦~~~'), - ], - ), + ), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('页面二'), + Text('左右滑动切换页面哦~~~'), + ], ), - Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text('页面三'), - Text('左右滑动切换页面哦~~~'), - ], - ), + ), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text('页面三'), + Text('左右滑动切换页面哦~~~'), + ], ), - ]), + ), + ], + ), bottomNavigationBar: BottomNavigationBar( currentIndex: _index, type: BottomNavigationBarType.shifting, @@ -59,6 +63,7 @@ class _IndexState extends State { setState(() { _index = index; }); + _pageController.jumpToPage(index); }, fixedColor: Colors.red, iconSize: 28, diff --git a/lib/widget/navigator/index.dart b/lib/widget/navigator/index.dart index 5746f20..9d38f01 100644 --- a/lib/widget/navigator/index.dart +++ b/lib/widget/navigator/index.dart @@ -6,6 +6,7 @@ import 'drawer/index.dart' as drawer; import 'floatingactionbutton/index.dart' as floatingactionbutton; import 'materialapp/index.dart' as materialapp; import 'navigator/index.dart' as navigator; +import 'pageview/index.dart' as pageview; import 'popupmenubutton/index.dart' as popupmenubutton; import 'tabbar/index.dart' as tabbar; import 'tabbarview/index.dart' as tabbarview; @@ -69,6 +70,11 @@ List widgets = [ code: 59484, // assessment title: navigator.Index.title, ), + ItemInfo( + widget: pageview.Index(), + code: 59488, // assessment + title: pageview.Index.title, + ), ]; List widgetMap = [ diff --git a/lib/widget/navigator/pageview/demo.dart b/lib/widget/navigator/pageview/demo.dart new file mode 100644 index 0000000..e2b7167 --- /dev/null +++ b/lib/widget/navigator/pageview/demo.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; + +class Index extends StatefulWidget { + @override + State createState() => _IndexState(); +} + +class _IndexState extends State { + int _index = 0; + bool _scrollDirection = true; + bool _pageSnapping = true; + + PageController _pageController; + @override + void initState() { + super.initState(); + _pageController = PageController(); + } + + String getScrollDirectionText () { + return _scrollDirection ? '左右' : '上下'; + } + + Axis getScrollDirection () { + return _scrollDirection ? Axis.horizontal : Axis.vertical; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('PageView'), + ), + body: PageView( + pageSnapping: _pageSnapping, // 与滚动行为相关 + reverse: false, // 滚动反方向 + scrollDirection: getScrollDirection(), + controller: _pageController, + onPageChanged: (index) { + setState(() { + _index = index; + }); + }, + children: [ + Center( + child: Column( + children: [ + Text('页面一'), + Text('${getScrollDirectionText()}滑动切换页面哦~~~'), + RaisedButton( + child: Text('当前为${getScrollDirectionText()}滑动,可点击切换'), + onPressed: () { + setState(() { + _scrollDirection = !_scrollDirection; + }); + }, + ), + ], + ), + ), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('页面二'), + Text('${getScrollDirectionText()}滑动切换页面哦~~~'), + RaisedButton( + child: Text('当前为属性值pageSnapping为${_pageSnapping},${_pageSnapping ? '滚动时,默认被捕获' : '由用户自行滚动'},可点击切换'), + onPressed: () { + setState(() { + _pageSnapping = !_pageSnapping; + }); + }, + ), + ], + ), + ), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text('页面三'), + Text('${getScrollDirectionText()}滑动切换页面哦~~~'), + ], + ), + ), + ], + ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: _index, + type: BottomNavigationBarType.shifting, + onTap: (index) { + setState(() { + _index = index; + }); + _pageController.jumpToPage(index); + }, + fixedColor: Colors.red, + iconSize: 28, + items: [ + BottomNavigationBarItem( + title: Text( + '导航一', + style: TextStyle(color: Colors.red), + ), + icon: Icon( + Icons.navigate_before, + color: Colors.redAccent, + ), + ), + BottomNavigationBarItem( + title: Text( + '导航二', + style: TextStyle(color: Colors.red), + ), + icon: Icon( + Icons.notifications_active, + color: Colors.redAccent, + ), + ), + BottomNavigationBarItem( + title: Text( + '导航三', + style: TextStyle(color: Colors.red), + ), + icon: Icon( + Icons.navigate_next, + color: Colors.redAccent, + ), + ), + ], + ), + ); + } +} diff --git a/lib/widget/navigator/pageview/index.dart b/lib/widget/navigator/pageview/index.dart new file mode 100644 index 0000000..28a357f --- /dev/null +++ b/lib/widget/navigator/pageview/index.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'demo.dart' as Demo; + +class Index extends StatefulWidget { + static String title = 'PageView'; + static String mdUrl = 'docs/widget/navigator/pageview/index.md'; + static String originCodeUrl = 'https://docs.flutter.io/flutter/widgets/PageView-class.html'; + + @override + _IndexState createState() => new _IndexState(); +} + +class _IndexState extends State { + @override + Widget build(BuildContext context) { + return WidgetComp.Index( + title: Index.title, + originCodeUrl: Index.originCodeUrl, + mdUrl: Index.mdUrl, + demoChild: [ + Demo.Index(), + ], + ); + } +} diff --git a/lib/widget/navigator/popupmenubutton/demo.dart b/lib/widget/navigator/popupmenubutton/demo.dart index acbff37..a35de0e 100644 --- a/lib/widget/navigator/popupmenubutton/demo.dart +++ b/lib/widget/navigator/popupmenubutton/demo.dart @@ -6,9 +6,36 @@ class Index extends StatefulWidget { } class _IndexState extends State { + final GlobalKey _menuKey = GlobalKey(); + PopupMenuButton popButton; + dynamic _value; + @override void initState() { super.initState(); + popButton = PopupMenuButton( + onSelected: (val) { + setState(() { + _value = val; + }); + }, + child: Icon(Icons.settings), + key: _menuKey, + itemBuilder: (context) => [ + PopupMenuItem( + value: '设置1', + child: Row( + children: [ + Icon( + Icons.settings, + color: Colors.red, + ), + Text("设置1"), + ], + ), + ), + ], + ); } @override @@ -16,9 +43,63 @@ class _IndexState extends State { return Scaffold( appBar: AppBar( title: Text('PopupMenuButton'), + actions: [ + PopupMenuButton( + onSelected: (val) { + print('点击后接收到的key $val'); + setState(() { + _value = val; + }); + }, + itemBuilder: (context) { + return [ + PopupMenuItem( + value: '设置2', + child: Row( + children: [ + Text("设置2"), + Icon( + Icons.settings, + color: Colors.red, + ), + ], + ), + ), + PopupMenuItem( + value: '设置3', + child: Text('设置3'), + ) + ]; + }, + ), + ], ), body: Center( - child: Text('更新中'), + child: Column( + children: [ + ListTile( + trailing: popButton, + // onTap: () { + // dynamic _state = _menuKey.currentState; + // print('showButtonMenu $_state'); + // // _state.showButtonMenu(); + // }, + ), + Text( + '当ListTile绑定了trailing属性,就可以通过currentState.showButtonMenu()触发', + textAlign: TextAlign.center, + ), + RaisedButton( + child: Text("点击显示PopupMenu"), + onPressed: () { + dynamic _state = _menuKey.currentState; + print('showButtonMenu $_state'); + _state.showButtonMenu(); + }, + ), + Text('${_value !=null ? '你点击了 ' + _value : '当前未选择内容'}'), + ], + ), ), ); } diff --git a/pubspec.yaml b/pubspec.yaml index f827675..9ad003f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -108,6 +108,7 @@ flutter: - docs/widget/navigator/floatingactionbutton/ - docs/widget/navigator/materialapp/ - docs/widget/navigator/navigator/ + - docs/widget/navigator/pageview/ - docs/widget/navigator/popupmenubutton/ - docs/widget/navigator/tabbar/ - docs/widget/navigator/tabbarview/ From daa0f06ec7a9e7bd5543bd12a5b3c25f51682dc9 Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 19 Mar 2019 18:02:20 +0800 Subject: [PATCH 010/100] =?UTF-8?q?refact:=E5=8D=87=E7=BA=A7=E5=AE=89?= =?UTF-8?q?=E5=8D=93=20compileSdkVersion=20=E5=88=B0=2028=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81android=20x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 10 +++++----- android/gradle.properties | 2 ++ lib/page/component/tabs.dart | 1 + pubspec.yaml | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 7108555..4448699 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 27 + compileSdkVersion 28 lintOptions { disable 'InvalidPackage' @@ -42,10 +42,10 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.flutter.beer" minSdkVersion 16 - targetSdkVersion 27 + targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } signingConfigs { @@ -79,8 +79,8 @@ flutter { dependencies { testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation 'androidx.test:runner:1.1.0-alpha4' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4' //firebase implementation 'com.google.firebase:firebase-core:16.0.8' //Crashlytics SDK diff --git a/android/gradle.properties b/android/gradle.properties index 8bd86f6..4d3226a 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1 +1,3 @@ org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true \ No newline at end of file diff --git a/lib/page/component/tabs.dart b/lib/page/component/tabs.dart index e59b3b3..f3cb78f 100644 --- a/lib/page/component/tabs.dart +++ b/lib/page/component/tabs.dart @@ -70,6 +70,7 @@ class _IndexState extends State Widget _TabBar() { return TabBar( + indicatorColor: Color(AppTheme.secondColor), controller: _tabController, isScrollable: true, tabs: _mapList.map((v) { diff --git a/pubspec.yaml b/pubspec.yaml index f827675..1f953d6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ description: A new Flutter project. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # Read more about versioning at semver.org. -version: 1.0.0+1 +version: 1.0.1+1 environment: sdk: ">=2.1.0 <3.0.0" From 5ffde01857836d6b38648f33c129da02d5b84d89 Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 19 Mar 2019 19:24:00 +0800 Subject: [PATCH 011/100] =?UTF-8?q?refact:=E5=A2=9E=E5=8A=A0=E4=B8=AD?= =?UTF-8?q?=E6=96=87=E5=AF=BC=E8=88=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/component/tabs.dart | 4 ++-- lib/widget/navigator/index.dart | 6 +++--- locale/en.json | 13 +++++++++++-- locale/zh.json | 13 +++++++++++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/lib/page/component/tabs.dart b/lib/page/component/tabs.dart index f3cb78f..8036bb1 100644 --- a/lib/page/component/tabs.dart +++ b/lib/page/component/tabs.dart @@ -5,7 +5,7 @@ import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'package:efox_flutter/widget/index.dart' as WidgetRoot; import 'package:efox_flutter/router/index.dart' show FluroRouter; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -import 'package:efox_flutter/components/headerComp.dart' as Header; +//import 'package:efox_flutter/components/headerComp.dart' as Header; class Index extends StatefulWidget { final MainStateModel model; @@ -75,7 +75,7 @@ class _IndexState extends State isScrollable: true, tabs: _mapList.map((v) { return new Tab( - text: v.typeName, + text: AppLocalizations.$t(v.typeName), /* icon: Icon( IconData( v.code, diff --git a/lib/widget/navigator/index.dart b/lib/widget/navigator/index.dart index 9d38f01..e8e706b 100644 --- a/lib/widget/navigator/index.dart +++ b/lib/widget/navigator/index.dart @@ -37,7 +37,7 @@ List widgets = [ ), ItemInfo( widget: drawer.Index(), - code: 58928, // airline_seat_flat + code: 58928, // airline_seat_flat title: drawer.Index.title, ), ItemInfo( @@ -62,7 +62,7 @@ List widgets = [ ), ItemInfo( widget: popupmenubutton.Index(), - code: 57440, // art_track + code: 57440, // art_track title: popupmenubutton.Index.title, ), ItemInfo( @@ -81,7 +81,7 @@ List widgetMap = [ ItemListInfo( nameSpaces: nameSpaces, widgetList: widgets, - typeName: 'navigator', + typeName: 'Navigator', code: 58717, ) ]; diff --git a/locale/en.json b/locale/en.json index b09f79a..ab16a26 100644 --- a/locale/en.json +++ b/locale/en.json @@ -6,7 +6,7 @@ "widgetType": { "regularLayout": "common layout" }, - "common" : { + "common": { "changeLanguage": "显示中文", "changeVersion": "checkVersion", "compProgress": "Components Progress" @@ -15,5 +15,14 @@ "loadNetwork": "Load Network Document Resources", "loadLocal": "Load Local Document Resources" }, - "loading": "Loading" + "loading": "Loading", + "ScrollComponents": "ScrollComponents", + "Common": "Common", + "Form": "Form", + "Regular": "Regular", + "Navigator": "Navigator", + "BulletBox": "BulletBox", + "Animate": "Animate", + "Gestures": "Gestures", + "Vision": "Vision" } \ No newline at end of file diff --git a/locale/zh.json b/locale/zh.json index c644e29..9e98179 100644 --- a/locale/zh.json +++ b/locale/zh.json @@ -6,7 +6,7 @@ "widgetType": { "regularLayout": "常规布局" }, - "common" : { + "common": { "changeLanguage": "Switch to English", "changeVersion": "更新版本", "compProgress": "组件进度" @@ -15,5 +15,14 @@ "loadNetwork": "加载网络文档资源", "loadLocal": "加载本地文档资源" }, - "loading": "加载中" + "loading": "加载中", + "ScrollComponents": "滚动", + "Common": "通用", + "Form": "表单", + "Regular": "布局", + "Navigator": "导航", + "BulletBox": "窗口", + "Animate": "动画", + "Gestures": "手势", + "Vision": "视觉" } \ No newline at end of file From 0e53181d7e6c5afa91fb60a9cb2012ba01a7e7d2 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Tue, 19 Mar 2019 19:51:38 +0800 Subject: [PATCH 012/100] =?UTF-8?q?feat:=E4=BC=98=E5=8C=96tab=E2=80=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/component/tabs.dart | 1 - lib/page/home.dart | 3 +- lib/page/mine/index_1.dart | 206 +++++++++++++++++++++++ lib/store/models/config_state_model.dart | 2 +- locale/en.json | 8 + locale/zh.json | 12 +- 6 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 lib/page/mine/index_1.dart diff --git a/lib/page/component/tabs.dart b/lib/page/component/tabs.dart index e59b3b3..0a7e29c 100644 --- a/lib/page/component/tabs.dart +++ b/lib/page/component/tabs.dart @@ -5,7 +5,6 @@ import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'package:efox_flutter/widget/index.dart' as WidgetRoot; import 'package:efox_flutter/router/index.dart' show FluroRouter; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -import 'package:efox_flutter/components/headerComp.dart' as Header; class Index extends StatefulWidget { final MainStateModel model; diff --git a/lib/page/home.dart b/lib/page/home.dart index 54f6c09..56ee524 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -4,8 +4,9 @@ import 'package:efox_flutter/store/index.dart' show Store; import 'package:efox_flutter/controller/index.dart' as Controller; //import 'component/index.dart' as TabIndex; +// import 'mine/index.dart' as MyIndex; import 'component/tabs.dart' as TabIndex; -import 'mine/index.dart' as MyIndex; +import 'mine/index_1.dart' as MyIndex; class Index extends StatefulWidget { @override diff --git a/lib/page/mine/index_1.dart b/lib/page/mine/index_1.dart new file mode 100644 index 0000000..80928d7 --- /dev/null +++ b/lib/page/mine/index_1.dart @@ -0,0 +1,206 @@ +import 'package:flutter/material.dart'; +import 'package:efox_flutter/lang/index.dart' show AppLocalizations; +import 'package:efox_flutter/router/index.dart' show FluroRouter; +import 'package:efox_flutter/config/theme.dart' show AppTheme; + +class _IndexState extends State { + @override + void initState() { + super.initState(); + } + + List _getList() { + return [ + { + 'name': AppLocalizations.$t('common_mine_1.language'), + 'icon': 59540, // language + 'index': 0 + }, + { + 'name': AppLocalizations.$t('common_mine_1.environment'), + 'icon': 57539, // import_export + 'index': 1, + }, + { + 'name': AppLocalizations.$t('common_mine_1.compProgress'), + 'icon': 57709, // low_priority + 'index': 2 + } + ]; + } + + actionsEvent(int index) { + print('index $index'); + switch (index) { + case 0: + this.openLanguageSelectMenu(); + break; + case 1: + this.openEnvSelectMenu(); + break; + case 2: + FluroRouter.router.navigateTo( + context, + '/webview?url=${Uri.encodeComponent(widget.model.config.state.env.githubWeb)}&title=${Uri.encodeComponent(AppLocalizations.$t('common.compProgress'))}', + ); + break; + } + } + + void pop([message]) { + Navigator.pop(context); + if (message != null) { + Scaffold.of(context).showSnackBar(new SnackBar( + content: new Text(message), + )); + } + } + + /** + * 国际化 + */ + void openLanguageSelectMenu() async { + await showModalBottomSheet( + context: context, + builder: (BuildContext bc) { + return Container( + child: Wrap( + children: [ + ListTile( + leading: Icon(Icons.label_outline), + title: Text( + AppLocalizations.$t('common_mine_1.cn'), + ), + onTap: () { + AppLocalizations.changeLanguage(Locale('zh')); + this.pop(AppLocalizations.$t('common_mine_1.success')); + }, + ), + ListTile( + leading: Icon(Icons.label_outline), + title: Text(AppLocalizations.$t('common_mine_1.en')), + onTap: () { + AppLocalizations.changeLanguage(Locale('en')); + this.pop(AppLocalizations.$t('common_mine_1.success')); + }, + ), + ], + ), + ); + }, + ); + } + + /** + * 环境选择 + */ + void openEnvSelectMenu() async { + await showModalBottomSheet( + context: context, + builder: (BuildContext bc) { + return Container( + child: Wrap( + children: [ + ListTile( + leading: Icon(Icons.label_outline), + title: Text( + AppLocalizations.$t('mine.loadNetwork'), + ), + onTap: () { + widget.model.dispatch('config', 'setEnv', true); + this.pop(AppLocalizations.$t('common_mine_1.success')); + }, + ), + ListTile( + leading: Icon(Icons.label_outline), + title: Text(AppLocalizations.$t('mine.loadLocal')), + onTap: () { + widget.model.dispatch('config', 'setEnv', false); + this.pop(AppLocalizations.$t('common_mine_1.success')); + }, + ), + ], + ), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(10) + ), + color: Colors.red, + ), + height: 240, + child: Stack( + alignment: const FractionalOffset(0.8, 0.8), + children: [ + Row( + children: [ + Image.network( + 'https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png', + width: 80.0, + height: 80.0, + fit: BoxFit.cover, + ), + SizedBox( + width: 10, + ), + Text( + 'Hello Guest', + style: TextStyle(color: Colors.white), + ) + ], + ) + ], + ), + ), + ListView.builder( + shrinkWrap: true, + itemCount: _getList().length * 2, + itemBuilder: (context, index) { + double _index = index / 2; + if (index % 2 == 0) { + dynamic item = _getList()[_index.toInt()]; + return ListTile( + onTap: () { + actionsEvent(item['index']); + }, + leading: Icon( + IconData( + item['icon'], + fontFamily: 'MaterialIcons', + matchTextDirection: true, + ), + ), + title: Text(item['name']), + ); + } else { + return Divider( + color: Color(AppTheme.lineColor), + ); + } + }, + ), + ], + ), + ); + } +} + +class Index extends StatefulWidget { + final dynamic model; + + Index({Key key, this.model}) : super(key: key); + + @override + _IndexState createState() => _IndexState(); +} diff --git a/lib/store/models/config_state_model.dart b/lib/store/models/config_state_model.dart index 8ae30c9..1e5adee 100644 --- a/lib/store/models/config_state_model.dart +++ b/lib/store/models/config_state_model.dart @@ -32,7 +32,7 @@ class ConfigModel { switch (name) { case 'setEnv': - _appConfigInfo.isPro = !_appConfigInfo.isPro; + _appConfigInfo.isPro = payload; break; case 'setVersion': _appConfigInfo.version = await this.getVersion(); diff --git a/locale/en.json b/locale/en.json index b09f79a..55a53e6 100644 --- a/locale/en.json +++ b/locale/en.json @@ -11,6 +11,14 @@ "changeVersion": "checkVersion", "compProgress": "Components Progress" }, + "common_mine_1" : { + "cn": "CN", + "en": "EN", + "language": "Select Language", + "environment": "Select Environment", + "compProgress": "Components Progress", + "success": "Success To Change " + }, "mine": { "loadNetwork": "Load Network Document Resources", "loadLocal": "Load Local Document Resources" diff --git a/locale/zh.json b/locale/zh.json index c644e29..efb3770 100644 --- a/locale/zh.json +++ b/locale/zh.json @@ -11,9 +11,17 @@ "changeVersion": "更新版本", "compProgress": "组件进度" }, + "common_mine_1" : { + "cn": "CN", + "en": "EN", + "language": "选择语言", + "environment": "选择环境", + "compProgress": "组件进度", + "success": "切换成功" + }, "mine": { - "loadNetwork": "加载网络文档资源", - "loadLocal": "加载本地文档资源" + "loadNetwork": "网络优良,可选择加载线上文档资源", + "loadLocal": "网络较差时,可选择加载本地文档资源" }, "loading": "加载中" } \ No newline at end of file From 95433de46abcae1e675256ac1fd615b43f7f7082 Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 19 Mar 2019 20:01:09 +0800 Subject: [PATCH 013/100] =?UTF-8?q?style:=E4=BC=98=E5=8C=96my?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/components/widgetComp.dart | 8 +++---- lib/page/mine/index_1.dart | 38 ++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/lib/components/widgetComp.dart b/lib/components/widgetComp.dart index 4283169..45df4f3 100644 --- a/lib/components/widgetComp.dart +++ b/lib/components/widgetComp.dart @@ -64,9 +64,7 @@ class IndexState extends State { this._bodyList.length = 0; String mdText = await this.getMdFile(this.mdUrl); if (mdText.length > 30 || !this.model.config.state.isPro) { - this - ._bodyList - .add(await MarkDownComp.Index(mdText)); + this._bodyList.add(await MarkDownComp.Index(mdText)); // demo if (this.demoChild != null && this.demoChild.length > 0) { this.demoChild.forEach((Widget item) { @@ -87,9 +85,9 @@ class IndexState extends State { this.model = model; return Scaffold( appBar: AppBar( - title: Header.Index( + /* title: Header.Index( this.title, - ), + ), */ actions: this.getActions( context, ), diff --git a/lib/page/mine/index_1.dart b/lib/page/mine/index_1.dart index 80928d7..c6c4e77 100644 --- a/lib/page/mine/index_1.dart +++ b/lib/page/mine/index_1.dart @@ -128,7 +128,41 @@ class _IndexState extends State { @override Widget build(BuildContext context) { - return SingleChildScrollView( + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.$t('nav_title_1')), + elevation: 0, + centerTitle: true, + ), + body: ListView.builder( + shrinkWrap: true, + itemCount: _getList().length * 2, + itemBuilder: (context, index) { + double _index = index / 2; + if (index % 2 == 0) { + dynamic item = _getList()[_index.toInt()]; + return ListTile( + onTap: () { + actionsEvent(item['index']); + }, + leading: Icon( + IconData( + item['icon'], + fontFamily: 'MaterialIcons', + matchTextDirection: true, + ), + ), + title: Text(item['name']), + ); + } else { + return Divider( + color: Color(AppTheme.lineColor), + ); + } + }, + ), + ); + /* return SingleChildScrollView( child: Column( children: [ Container( @@ -192,7 +226,7 @@ class _IndexState extends State { ), ], ), - ); + ); */ } } From 0e101c44f93ed5d381b2094738f753030aec412d Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Tue, 19 Mar 2019 20:05:17 +0800 Subject: [PATCH 014/100] =?UTF-8?q?style:=20=E4=BF=AE=E6=94=B9=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/mine/index_1.dart | 53 ++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/lib/page/mine/index_1.dart b/lib/page/mine/index_1.dart index 80928d7..244e1ce 100644 --- a/lib/page/mine/index_1.dart +++ b/lib/page/mine/index_1.dart @@ -133,32 +133,41 @@ class _IndexState extends State { children: [ Container( decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(10), - bottomRight: Radius.circular(10) - ), - color: Colors.red, - ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(10), + ), + color: Colors.red, + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [Colors.red, Colors.blue])), height: 240, child: Stack( alignment: const FractionalOffset(0.8, 0.8), children: [ - Row( - children: [ - Image.network( - 'https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png', - width: 80.0, - height: 80.0, - fit: BoxFit.cover, - ), - SizedBox( - width: 10, - ), - Text( - 'Hello Guest', - style: TextStyle(color: Colors.white), - ) - ], + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Opacity( + child: Image.network( + 'https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png', + width: 80.0, + height: 80.0, + fit: BoxFit.cover, + ), + opacity: 1, + ), + SizedBox(height: 10,), + Text("Flutter-UI", + style: TextStyle( + color: Colors.white, + fontSize: 26, + fontWeight: FontWeight.bold + ),) + ], + ), ) ], ), From 54fccec529329274f28f156e871436dd8f48c4ae Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Tue, 19 Mar 2019 20:08:44 +0800 Subject: [PATCH 015/100] =?UTF-8?q?style=EF=BC=9A=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/mine/index_1.dart | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/lib/page/mine/index_1.dart b/lib/page/mine/index_1.dart index fca2c83..244e1ce 100644 --- a/lib/page/mine/index_1.dart +++ b/lib/page/mine/index_1.dart @@ -128,41 +128,7 @@ class _IndexState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(AppLocalizations.$t('nav_title_1')), - elevation: 0, - centerTitle: true, - ), - body: ListView.builder( - shrinkWrap: true, - itemCount: _getList().length * 2, - itemBuilder: (context, index) { - double _index = index / 2; - if (index % 2 == 0) { - dynamic item = _getList()[_index.toInt()]; - return ListTile( - onTap: () { - actionsEvent(item['index']); - }, - leading: Icon( - IconData( - item['icon'], - fontFamily: 'MaterialIcons', - matchTextDirection: true, - ), - ), - title: Text(item['name']), - ); - } else { - return Divider( - color: Color(AppTheme.lineColor), - ); - } - }, - ), - ); - /* return SingleChildScrollView( + return SingleChildScrollView( child: Column( children: [ Container( @@ -235,7 +201,7 @@ class _IndexState extends State { ), ], ), - ); */ + ); } } From 1121e355a31bbf3e7d46dbca429c7b66f80ebe8b Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Tue, 19 Mar 2019 20:12:02 +0800 Subject: [PATCH 016/100] =?UTF-8?q?feat:=E4=BF=AE=E6=94=B9=E7=BA=BF?= =?UTF-8?q?=E4=B8=8A=E7=BA=BF=E4=B8=8B=E7=8E=AF=E5=A2=83=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/config/index.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/config/index.dart b/lib/config/index.dart index 08e9168..350fcd5 100644 --- a/lib/config/index.dart +++ b/lib/config/index.dart @@ -1,6 +1,6 @@ import 'development.dart' as Development; import 'production.dart' as Production; -const bool isPro = false; +const bool isPro = true; Object env = isPro ? Production.Config() : Development.Config(); From b9891314d97da2e77c769fc9ba845adc4b52db51 Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 19 Mar 2019 20:18:57 +0800 Subject: [PATCH 017/100] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 557e220..2621446 100644 --- a/readme.md +++ b/readme.md @@ -13,7 +13,7 @@ [历史版本](https://github.com/efoxTeam/flutter-ui/releases) ## 开发者如何参与完善控件 - + 欢迎有兴趣的小伙伴QQ扫描以上二维码参与进来,一同完善组件 同时也可以把相关问题通过[issues](https://github.com/efoxTeam/flutter-ui/issues)方式与我们联系 From b6e5375582c98ab106c6878aa6b3d3615af25045 Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 19 Mar 2019 20:21:27 +0800 Subject: [PATCH 018/100] v1.0.1 ~ --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 2621446..557e220 100644 --- a/readme.md +++ b/readme.md @@ -13,7 +13,7 @@ [历史版本](https://github.com/efoxTeam/flutter-ui/releases) ## 开发者如何参与完善控件 - + 欢迎有兴趣的小伙伴QQ扫描以上二维码参与进来,一同完善组件 同时也可以把相关问题通过[issues](https://github.com/efoxTeam/flutter-ui/issues)方式与我们联系 From ebd86d241d4be20aae2498f2ca1bd6e3d0fc2519 Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 19 Mar 2019 20:22:41 +0800 Subject: [PATCH 019/100] v1.0.1 --- readme/apk.png | Bin 6021 -> 5975 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/readme/apk.png b/readme/apk.png index d6458efef9ad5b6dcb4b7b981622e28c1fdb1ab2..da4584bc2e7b9c5fe89f937536ab47d963fd2191 100644 GIT binary patch literal 5975 zcmYj#2{e@N`~EXyrp8FdGLSKz^E}@j{$ySLL>G#g(d;aJA&wJ*~oacGp=e_5CuIIY0`%ZAMGZ#clpaB4amKLT? z0KlN1Fo4P4TWf`0m2-5sWRfB()e-`p4qK9QhM1Kh37_}Vf! zG;%o4(8+quRY`Y&Q8E-UTylrgQ* z*>l>Pb7)@h$@1w4MY5DJS@QOn3aCXn$U1@j1M}g!!eWfJXRRjfl@(rAwd)iSABO*Utya;v!SgEyWZT{%Bx-@EJ7cWLWUH znL&}^%777?WA|M@sRk@Q_~0nm@?~L~%>AgB*Cz&$6A9pO6Cu|L)|tUq3-sOT>GyvV z$dx5v+MB4H#inQa+X3EnW%&!*LDtf^aE)Dlcf=z!H!qbD!D>onB(P|853=oK#uTW4 z5Tg5av_KN8Go~0?-Eq2ah~G3dZBH@u&gBtE+v|5(lriPNnNC^T%m zk277^^#JXc-ln69Wr&vy8?nMDsf^Ndjnp7`!L)wH6*q<(0+p(4KmW;6=w_mB@J=5X zJxzYv8LSJ>i+%im%W;Stn=5+@2Tvlg+{A5%tk}i%gvr`?XP5&JV>~Hjw5BsufHn1x zmnp1dL3+o^(APxK+q1U*MIcoB*qqNw4m%%+;DJ?;pLt%&n2Uke&9*>WGQaI!YU4)k zjG2&DqUQL@^JoE5j1rxFv$6n=sdC!TqTsOvzi0u>l*z9XW1T~Gu=raJFjUI0o0Ga{ z+a5~Kz4Bz6B{7D3yYls*sG1=oADEw)aTPIsz}7QUcQg3$X|;5j)2a>GXF;a$cKVv| zm0fCjn??FaT#9yO6}&FB@hmXj>c1v<=9~L_TThXxSJx&fi^@KYQYnVDx;cap=J|h+ zBb5fSN{V|F`SfIiZ)oZ#?yfJn*d5{kwu~8s7q=MY^%y_b#e-iffkTHsoh?-1t=iK2 zI@Aac@Zd!=OB!qAH;1V6-H)8mN;Z}p)i8Gzc z%NHGO=4C_5#`;@|%}HpjrV`SJg1*cgtgdt6sIv{lR;4F18|zL0`Y38zSq3zdNeh3T zG;4d_6$-;xAH6otZ<`<IQISDb=Czld$TEOD5n^sNMmk~FN zzdU-4H8LUzLKE5 zUHt<5B2rOOl%UI&ZC#qY_hYXBk_5Xb*Eg;ZVB}}QoxL$0Ii~lh3eE(|K<0?#MP&TT z=SbD%b8_!wpJ}qZ1EUKf3!Y5F@70UNiZY;!V}A!kXQSZ80LrC!NC+c-+JHs z_6zp8D~*#TFy>Oj85WRAJA3Zy@ef^CfTXqiXSfjnnslIS;GG_<)Hq_%0s_25(6##x z0CP%19*QVlqx@_WypPcY1)|lr<8%?gjrf_N8E}lh&4-0GBb3t@%_(`^`8VVtyow+e zG`u-J_-E4F@mq=1p^dS+#rLVh`4FY)cOKJ8gJTMT7#JJS(Eo4YFm!B^ksRuU~bsh zCk3 zyzikkJUn5LA2&?00~&4fC-5NAD_t5=3u(bT^uBR>7_*bRD8|_1^QkIW-dR5v+C!6+ z$r&u}n&#H;@NrTkG9Ek3kGrrjz9X#a}!~Y%HO;@uQ)_BIAo_`Co1X^0&g^X3NQI$Ynd(f4%Jvx$!isPxXEPxPZ zk+wgGco|~lIhLtl+2R&HuAGI)NGFmu!q-bWTKy|W)FMlzKZa#16$KbIl-P#vK3`Em z{B?|(ND_m@iGZ@@?rZfRZn{i_W$OleV;Kc#XLWv9WzHek8mb&#cLgZ!lWrnNG{|j7 zGK;;3qeM1)-xa=*|J~9cXE!F#(^q>$1!>T%^L|xO_2M3#%h>Y-V4G}%-!20qdRA@) z+>7^wp^B|uFIhqOshg)oQ2b-m%ayR_ooX&`ZtM37rXoJt3z*7`#7`yu2#yL;4K<40 z-BctvkZeiGFZrbmjLnW^6o^!lOM_HLa}d@I?6%_imXoB~LNrH&GQ10#)b`f-NmxH( zVi0%>5}p!KQkpNCL!|xhZZC*cvf>XM$+V^88RC3O?&%nw#-l3vyodrbf z5ib*g$m#d|G~pi^F9{M=lisCrJ+3e&{|!#5VeNpIR?4aRQT1~7fvu9?(xd&^=Om2o z8PnfuZQb_%C6BZ9g3XKHBQ6Yj=p$*v1NFUF20CaHo;he4GEsW~b5ZK3>Z0)PD8o`UZ>8;V7r?`l5sU9DZ7*bUw(U{j>j9*F|pH zFRwoZSJ$0HIqTitY>Tz3QgrJX^~Q}VPAU0Zv6P30z0yv(9mM*ygJOXvvb)=PFIq(t z6+N_zR2(a$gJU(dM=us6Tpe-B=XsTC5!sRZSK%0h#D4tuqNW(xo!vnYgCKG46tX%J zhx(c@Q)&nu=m~^%E!aL?rXWvQ6~vX}*xL2p!>)rF`D1#p>en;PiCU$QKb;nRX&J@a z`(rPJV^zS*)jK{G3nf570Q|gy!YlwgDIpteaKE#5bD^Fi(bO9puZb0P8kc$8uO_F;BeR5h(7?WTYaUL)W zgtOZ|$YCU?6F~xP;Vv>pW*?Z&jv+iEml%H15edII|9O|QC*K%pXj$1V(TzKGnxd*` zh-J7PIwqR2*PlHRYJ|iI>3Qn^qy7=nGc*Uiq@qDw%m=Q7_2%R5d=R4Ci<~L)f%cwQ z)-qZ<@+84e5S@6<$CN`aWj$BHGuoc}C6>W4r5ear{5Z39nnzZaK1>&;DqPm+ z@u0nd!X+}?7%s)m%Mr*`blr>kf5Spgl}Yk$aacB3s_>)RVR__IWy-qrh>PeON39X_ zU;F)Ouklk`XndN2p6yJrk#d{Fa7$Ya=Tl?TCtpd-S#eA1rDiOWtRzdPOM%gpdZcF2 zPw2F^^#AHJeCQM<#*o%_lv4rqkyY^3MACyjyj;G0Z^ILXgiD#q*plHzLgR*>%Xi|8 zx5o0oW*fXC&OiQ}OZ09=$~e2Z0)nFnv$zII)~YE(`#@7t|KGo_v5}62Sh~E87-OW> zB0Or~0|r(o3Zb$E8Z}3it2t^R{%MVZ@Q#-aH zarE@w)mK~j!w^BZ#)5lb@rsS<)rrSTb|)RqxAQxl3D)CMVD7OS7ldKC>8VnSV)n<+AkVL}cJ7xRzF?zrrW@J>V7z;JfrpZTesu$Ci$uv)A~6@5H*nQMAPShFC# zAIr?6VSlRt4G99I?JaH)isJ(_3Vv1TMs*@I^ZF`!JJN48->opF)Iqqv%RZV)W^NHW z>-}rnzT5c*vWPk*sZ{V4 z6}4Nne+9l7Kn@fDW$&cp&b~|zx$0#cJm^@oR3kku)DClS2ofg5I|(Pj)53dMa#?>? z&6@2b59ptjcy7Y1{kq*4s;$IT5-OyH4e^YqA;JlafA8E0>=jQVpq&4hAeZB2aim~T>rF15q>Cxzvjc!B%CQwB|{-W(OoTRNL%i%h7PNZaNF zj$@<$T#CC5$8>169&P;7J>UqAqwZa{L!6pzf6IcaS9KZ{2I`!;|JdUYnzN(mdv9xA zQ!yRQ`KMim*eI#}_Je8xS|kE_k-~(}p&^m3_Rj}L{`V{l zGLgq1u4z+NVt?_&=GB&;@4H3?vj=}R#O~_k$D!6}5#fRr6=^r1MglOFgf>nEfJMHH zWeH_adzBeGg{55|!qR4&-u}Hq$W3|XE^K|(K3yjHN=PS>RA_%cun z5D}GN`fwYv=<@!Rniw}mIg!(^#^-{$N`Q{H9T=T1Detoi_-vP=2=Y0k#IiR!$rdG6sWrq$a4whz(HFDT$U#ZA@B_8Er^b~SfXl&8@pl=w*hzgzTvXn#uyPkF(2e-|J6e~N`?Tx^ydWdL zlxS9dFrKp2oclF>_2gUZpZ&mg*@`X}l*^}rQIzRU?y&fhGkQ}>j3Iuvz6$F3NcEZs z@Sd!O?9&c4JVV!Kgba%}oV7NVK~r91?nWe%*3IzoA4ob&_dV=}l2J$%i!WBE+NC2e{CR z(hO{I`qo=UeP&N>`SfQ4#o&+2X>VPPC0KCGl+nyhULv4E=@RxLltXBD^)L1hvSRxn z27vC%6XisU4(9&eh7qM0L_@^IB(8?Q<6kq$)JuFpIfS5(`QZHr9JWc`_i~TYz6H@g zbIkgNd|z98nG@lLlPhyEBC3(4WyKb(-?fMizP?{Y%RvZNX5PP)7y1j~0IS*xEf0NKAl}ZYGUJAT2J_(zzET3UE^nxpPF(~~*(>62B#(P)muqhnRk zA@a^g@m!{0F+j+45kQG)5e1JZ(T4m|$f!ZFS7*wVuK~x+zg>Xm$R62jhHBgZ2OAj5 zjv&qq{K0EK?byrHNYR)?;^3{gM6DgCF}2{yv%8==uP=_~)=(6{((B6&d~P|lXQn`U z>i+KJjCEMI(4Rx6`XXm3$pO?qCI>-ZrZV`#~B%10KP6znpt3Rh&g$lP&>5pr4&qm^ofC7MYWKN z;fdWlRzhA9Ej}aC+>Rr0@rtJd{x^hWw53yNl>qVCMDq5oR3Bn&@ZqfRj2J&l-*3dP z`iW)5XT}ZruiriO==&Q+D73|7izEG^5U{-=f6zv-7_y}!C$KSzgDp?-!p?+uJhex{ ztQ5#&XB?}8hS7xxYfl}>aybAc=L=j@3yJ%!9+o!(-$(gnXMd}T;zK3M#I#Mul_vK| z3lyBo2dKeGZJvSdkiat(bM7Qn^gW~NJ(l3-{5CEy4C286ZKcfG)GGAP5@w*b!hN?Bhyw!yKOjrInh34di$*e(2V#;%IYjkQs1-)x zl@+lhq%6@N^>8*`yF$Ubj+BCG=Gq&HL+dW@u14|2kJmPK*)=<8 zd_e|pdfiFpPyW}OquIGMatQKZ)eZ~0;C1Pp*x|lfR;T5bgUyc(exVgwpD#CIIQiIm zoU5?WjAi&)2Scf7fxZGTURyjrgm>K`pCJ4B|x?ula`uBQd6{A6=a_3E(d*!)&RtIqHHU)G!B@6g$%zGA;K10OGr|&&)HQ=gc|xxwq@O?r>uYc@HZeD*#}Rfj-F;00cfF0Lu)& ztX%W10I=84fTVTW+h!(h#+Sd0Em-+)V7q#2mPCuE`f9BjMxD}LBO=a;5jo-cB_?ol zWuiy(=K8Omqr96RK72Sf{a;F5%yQ`o`N+$!bD6HjUGfsRzw6)|^JM|+TGdIO_D4Ub zgfT}}8;_#KWL|RBliwzC1v(?ECvd>zw7vq@J*5m7F^n2?g% zErD_%-onIFM|!c)Yb4N6^Y=D}MmO^^?VT0SE9)Q#WIhq&^_7oar}p3X!v~O4@uBl% z;t6gtN;iw7QxEb+h>P!A%6yf%hZ||L(4S6WA-?yyCWy4v9vmiz_aCvN6CBu*0VP&! z^nAM-`doXm^>I8OfiJT2`cmOwm;!3NY)5W(wT$$%1J9D1pb;Y9TR& z=lVuJ_#RA9W7#*RkS*5tt7wCph8nGl0@Z6cu;(R~cGvu=2Zk7`hB7YjB5l8~y`qCj zXEMNl76p7Z1>HCxI13k@!w}y;?NUV<@8NZhMFOh#D@!tPFBJoPSKvNHPZY0zZO>R7 zd2%i`ON&)x^^#P?-Z?ir1D8W{LVhZ)VSxL^LtZ5m-bQ@9v9d(d4pD`^YzRnP zz!0B`)<-Vuf)+=eH3?gc>f3k@w{Cg1FRCG@9M^6-{@W^{F z=+wyf!x`Sq{-M*{W8(FZtIfkCbHImP$|Ra;cev#HIz@xy3U|BjvufP>K2sf=H2S_hb@ef+>IhGV~GBrGbOY!40=YE0d(nR85))}#wv_5H;Vxd zLi-U^80AGckl=9~z@|=aKSqKun$`jqU?}c7sPOEZ{KAhbRr93^=&Inw@7;zl_V$MYaJ1P>t#7bZc<*Rq z)Lbz-HQI6|`>#JaDjCQlPF_~qvG`{12IjxBfed zI@`eOu*-=7bppFrINZX<;4QXRx;^l1AkU2c8#lw*@6zU=mX{nfc`LC2{n8-2ZIazQ zH{l8@3Fba>LU+{iY+5Zv11sI%8G+Cu=X=_T-#-NkE?a9POjjNtq^z5E?QB`hG)WN} zVvTlLHTgQ)6C~Awr|dj>e(UftsUL{^jws$ncpu(gVZ8DWvLkUp%qm?@547MKG0wM7 zDmGDr+w^h*@HbOzF@t5M^k6BjS_3kH)`DReoPsC*Cej{h@Qqa$h=JC|c0PM_^6eLs z$5x2zj#8O-+XnK6tDfrXvTxhJ>w4@Nwk}Pt)MGFAx$P*OkxdJW5m%L^$qc+DpXH?w4d0)2k%qqz)aV z@QKx%W_@^{THVfJNR4`|C^5iP`eLZD$K>lQp(&tbkgaku1xK6sNCtH`7^7To%A6=o zk64I~aV^0a)JXTHN4|+s> zxuHLc1MglGpXvvs<5Lca(!@ewizSc@v*z9lY^Z||4n!_S!06(+v`TzJ#j?t=bjPzb z$mv~w^RIOI6#dCu(FJE4Hzy{zsV)x_lgz$&KBBr8&6b5}eybU0$9_L)1d`s@QlASA$&}Mb{L+ZSjjb)|+@HH{E!lsrX=Um;5eoO#9 zVt7s9K(u)M(~g+Iz{~zQWh%p2k3Z{zy2EF8QI>qAwaqfZj#brtZ?~Y61YEw9>w!8} zb7iL`U+6bRQ-v?=-i zal@arb{~*d8UD;Om}SaM{lN5%3d_|xvfcM8%HjB1HY+`QwGFCKg>F0UDuyxD zE{6`@v@Z|NES@}bk8&XyunMgT*y#bP-zrS+iG35S!V#=KTgxn?ZmFJ|uLss9qYp5% z846f6@d%sK3T&pUf8E$|#(tFg3`;a|p+(p@&JDXx|a+acZ44eMqvPb&OWc%>J}QJ20-B@hEoLoMb6w5d**h6?`%7IBe(#(I=rfQn-&??5AK5t` z!R}L=ZJ~NdgL4Z<_OcFz<_B3xsm^7GcrM>thB@RPFptB zR$JF!zh6?Kfxj8|jps46^FV>)Az-qOm78MGiB$d8TAZWTrS5oJfyc~JD8lV;z+__3 zB+ESvy6e~-jVRu?$Lw+S=`lsL0#Mkp*jZHR2p+7K8v z3c2YKYd9c~AIKl8#D3%*zAQ#(;M!Ju1HZ0TkK1yW=qsBekUyAU?3y25-=+P$Yq(?% z@*!;OY8&&pt_SPKxoBaQUUS&DXQg0mVrRBeJ98iExWCIfp1y*U(JW#sLw|Dpd7gFW zpVhk%RTX2okNmXm5wmG5Q7~z=Geg4Ox)YL2*u-H$M1~_>Z`)ovD{TXkaGAT z3xj3SNlb(Ti5(nnzyZ4T@mp9D@c+75?27q6UR^;g{C)CRR-F{I28(#Xyix5B^LZY% zser|$KCQ4b^sAaR9d)C5T8b^)M?c1&GZHMe=#B!%6N^)lvR2rIES<{-KEsxre!`LL z((r1VBYWl8@N3uN2#ON z(wi~PHci-Cw)b0?&5R6P)*(*!14}cem2YO9uePUAPAfc+dRhb zZhtx~qe_-ZP;5HN)J`mMojunv+$X-1yzXwaGyQLV>DKsQQp;otTg@LS8I&kxIYNH; zATCC)??{rEm4Hu&q9Anqy>vP=r%tK;Y}n(%WEQ(XP8At3keFvXV%XGwD;MDXDz2M~voo!ivXB?{nK zY9}Ak7Iz6O^2EP3`)i;->|Uqe&dGli?y!c!gq|4;!)WT!{u^$NHyTnk2Xu~G2HUZ0KAMI23` z&UFVttG?Yz)q3Ipecu$kcO^oNN{=?&fK0V3_j`(x^#SCS=_o5>$ipHrkd=7m<52GV z;y=PMW^}H&i1(`R-6Chl`RWbka@T(p3c}hKM}3Ds*t?iB{Ch&254gc#SlUl0VA2;Dn!=3vi+6~a0v znxP&32r_G$ejj2N&lT-NOtE{#ex36}q}L(ZnOD~WD2`B`N$#f#m*Dy@*R&{u4-yzU zSIOPg{xgzCt4!33D0iUfIvc@C!4NU4hsJTVcx?iOi4}jnaTYpGd5S8x`y-dj!%ySM zfP{6p@MMq#>d?Nr)cGv6bu2B6aKs5itO$&_T$ZLXX5b~djh&WXA$uieSw{L`h#@K{ z0~z=J+b-%Itq2#CSOC?n!_R|BCB!a?m1l$ahW}DZ2kQqut6g-jGwma?&8l4Mj~*jq z7%A6Ur(xXXsw1&#!wkh35VkQ1EO%dUpj`Tlc^=~VO_-~e7@~kUxncSBAYc3$C&5)K zYr6F&vw3XanqGRxJ_RJqJ}K$yHc#G(;%Mb>B4*7Z8zJ2{1_7&0qy@3MERFkZoiWwk zUJ*Kfv%0L+$_MVZj_#o;Va0`x$cPIyx0(pM7BGA|o-~@6bMAuXS4r8h1*FFyQg(P3 zON(ACKW{ZS*I>uK^-SYPQ`5m4#g{1tqz%^{Uk`suxR=R%p8xSa zT|gRzU5{p>ZKf&D6Mi`$N$rPqnsW2Z0`U_+jrnmfhb5+_Kq@@X_7E`5b`v7+p}Iqt zmSKs9z&-g><8L^@d<|18V6fk$Hoy6L(HF0POpZR>7L0Q;OWUM=Ga3@8J2PEAok#?cUI0f8xBDIgJbY&%=lu zZr~WJ4ED@z>!cuB<`iBv;Jn&JmuCj^nJ%NO#5QIPufxWLz@r0OAk8rD2h7;rHCU2X zPX5=8SW**tyvv*d+L7mB0us)t>QmGXa5a%vlJ1xM7dt`B}b z)54Q-!`#&qwg;-O`93MU_>nU(_q%VirM|-2EO*DT5Gy)3@6bV;r}v*siz_IBEPNA4 zl(xlOgzi)HEd;%9LvpJP0bg4(z*pvri*V8C)P69}3`GU@>yY7wdwC1&I;t5Ry=S)0 zIjbLkOkR?#iEzi@Z`r%z(=vn3mPQ3ooYRvC>#7Sj;m2-`NN&9xJqTKltuT>IlFIyGdyz+e|!Y$34_dVf*o)Q+Wh&2aCO?$0gRnFbza-S<>T*R5anB&}w^qL{-8aQ%r0~wUr3^Ur4c%n=a@R^+ZdIkP6=7{o@BJl#moJch8|Ny53RfcpkV#6{mB38@ z2?x|LGg-p&Gu`T|Mhl-X&qIIBK(=RTKn*6r=1Dbld;wF22nR=%Y}1vY$bJe z%odXo)Fw)xXp?2K%JRMv^wn-1p#%M~Y<2Aw+RA!-4c0wZG>Vn9>i`nF>T>O8?L6iC zCycP`9i{Bj=3JG&Pg=c(AnW#o25f#O$ztE1U?Kcy@BZvyG!;2oK??e(?W2#a2y|+qiy`UpXJY58sOU5lnd!Wph*k{@m1E?X-cn@2?!P35B z*gP`rOU>H1smf7|fOv2dW8A>Je ze0}0)ekp_e8_KrQ+crT*#gB;Nc&cX!VqnK;uZ1Z39Et15swfr(E!SoXjOzVvdNdz~ zXPmhK=X=80Jz9tTE#3bgsef&AS}Ms)goGiA>A(OwUWW6>JFzPhP!bJ74bk&a6;3i6 z*B4-bo&UX8=cYXvu@0c{Q+YM3&T<&ek|xzI7!*?J%fH^m^Sr^@e1IyR%O1`_7{<3v zWSZb)>49Ntr^g~$Ybo1iWJpp~{D>9=XB9Z1j5fj)83Lv#v2AMFTNE$x)LRrtU`>ow zcRhr(r8d9JD}Yk=NSRiN9Oo$Q@QC2(#RVQfyhbPPDA6+@bo1DSen~riu3iVTV6hM= YkgN3M)#e2uI9>+^x)f5Ow%zUj0Xk=XCjbBd From 6fce8889c7f8e76749b0481865d53ff2dca05ff1 Mon Sep 17 00:00:00 2001 From: Magic Date: Wed, 20 Mar 2019 17:14:01 +0800 Subject: [PATCH 020/100] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/widget/vision/transform/index.md | 25 ++++++++++++++++++++++++- lib/config/index.dart | 2 +- lib/widget/vision/transform/demo.dart | 12 +++++++++--- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/docs/widget/vision/transform/index.md b/docs/widget/vision/transform/index.md index 83532f6..dc2da45 100644 --- a/docs/widget/vision/transform/index.md +++ b/docs/widget/vision/transform/index.md @@ -1 +1,24 @@ -## **文档完善中** \ No newline at end of file +## **Transform** + +> 在绘制子元素前应用转换的组件 + +### 构造方法 +``` +Transform({ + Key key, + @required Matrix4 transform, + Offset origin, + AlignmentGeometry alignment, + bool transformHitTests: true, + Widget child +}) +``` + +### 属性介绍 +origin:坐标系的原点(相对于此渲染对象的左上角)应用矩阵的原点 +alignment:原点的对齐方式 +transform: 在绘制过程中改变子元素的矩阵 +transformHitTests:在测试时是否执行转换 + + +### 实例 diff --git a/lib/config/index.dart b/lib/config/index.dart index 350fcd5..08e9168 100644 --- a/lib/config/index.dart +++ b/lib/config/index.dart @@ -1,6 +1,6 @@ import 'development.dart' as Development; import 'production.dart' as Production; -const bool isPro = true; +const bool isPro = false; Object env = isPro ? Production.Config() : Development.Config(); diff --git a/lib/widget/vision/transform/demo.dart b/lib/widget/vision/transform/demo.dart index 7774d92..6307466 100644 --- a/lib/widget/vision/transform/demo.dart +++ b/lib/widget/vision/transform/demo.dart @@ -15,10 +15,16 @@ class _IndexState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('SingleChildScrollView'), + title: Text('Transform'), ), - body: Center( - child: Text('更新中'), + body: Transform( + alignment: Alignment.center, + transform: Matrix4.skewY(0.1), + child: Container( + padding: const EdgeInsets.all(8.0), + color: const Color(0xFFE8581C), + child: const Text('Apartment for rent!'), + ), ), ); } From 40748dd79d24f17b606e2ed19c7e7b956520e94f Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Wed, 20 Mar 2019 17:25:44 +0800 Subject: [PATCH 021/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0gesturedetector?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 4 +- lib/components/widgetComp.dart | 1 - lib/config/index.dart | 2 +- lib/page/component/tabs.dart | 45 ------- lib/widget/gestures/gesturedetector/demo.dart | 118 +++++++++++++++++- .../gestures/gesturedetector/demo_drag.dart | 71 +++++++++++ .../gestures/gesturedetector/index.dart | 2 + locale/zh.json | 4 +- 8 files changed, 196 insertions(+), 51 deletions(-) create mode 100644 lib/widget/gestures/gesturedetector/demo_drag.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 2e9846d..f088201 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ + package="com.flutter.beer" + android:installLocation="preferExternal"> + + + + + + + + + + + + diff --git a/lib/components/expansionTile.dart b/lib/components/expansionTile.dart new file mode 100644 index 0000000..43ae1bc --- /dev/null +++ b/lib/components/expansionTile.dart @@ -0,0 +1,222 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +const Duration _kExpand = Duration(milliseconds: 200); + +/// A single-line [ListTile] with a trailing button that expands or collapses +/// the tile to reveal or hide the [children]. +/// +/// This widget is typically used with [ListView] to create an +/// "expand / collapse" list entry. When used with scrolling widgets like +/// [ListView], a unique [PageStorageKey] must be specified to enable the +/// [ExpansionTile] to save and restore its expanded state when it is scrolled +/// in and out of view. +/// +/// See also: +/// +/// * [ListTile], useful for creating expansion tile [children] when the +/// expansion tile represents a sublist. +/// * The "Expand/collapse" section of +/// . +class ExpansionTile extends StatefulWidget { + /// Creates a single-line [ListTile] with a trailing button that expands or collapses + /// the tile to reveal or hide the [children]. The [initiallyExpanded] property must + /// be non-null. + const ExpansionTile({ + Key key, + this.headerBackgroundColor, + this.leading, + @required this.title, + this.backgroundColor, + this.iconColor, + this.onExpansionChanged, + this.children = const [], + this.trailing, + this.initiallyExpanded = false, + }) : assert(initiallyExpanded != null), + super(key: key); + + /// A widget to display before the title. + /// + /// Typically a [CircleAvatar] widget. + final Widget leading; + + /// The primary content of the list item. + /// + /// Typically a [Text] widget. + final Widget title; + + /// Called when the tile expands or collapses. + /// + /// When the tile starts expanding, this function is called with the value + /// true. When the tile starts collapsing, this function is called with + /// the value false. + final ValueChanged onExpansionChanged; + + /// The widgets that are displayed when the tile expands. + /// + /// Typically [ListTile] widgets. + final List children; + + /// The color to display behind the sublist when expanded. + final Color backgroundColor; + + /// The color to display the background of the header. + final Color headerBackgroundColor; + + /// The color to display the icon of the header. + final Color iconColor; + + /// A widget to display instead of a rotating arrow icon. + final Widget trailing; + + /// Specifies if the list tile is initially expanded (true) or collapsed (false, the default). + final bool initiallyExpanded; + + @override + _ExpansionTileState createState() => _ExpansionTileState(); +} + +class _ExpansionTileState extends State + with SingleTickerProviderStateMixin { + static final Animatable _easeOutTween = + CurveTween(curve: Curves.easeOut); + static final Animatable _easeInTween = + CurveTween(curve: Curves.easeIn); + static final Animatable _halfTween = + Tween(begin: 0.0, end: 0.5); + + final ColorTween _borderColorTween = ColorTween(); + final ColorTween _headerColorTween = ColorTween(); + final ColorTween _iconColorTween = ColorTween(); + final ColorTween _backgroundColorTween = ColorTween(); + + AnimationController _controller; + Animation _iconTurns; + Animation _heightFactor; + Animation _borderColor; + Animation _headerColor; + Animation _iconColor; + Animation _backgroundColor; + + bool _isExpanded = false; + + @override + void initState() { + super.initState(); + _controller = AnimationController(duration: _kExpand, vsync: this); + _heightFactor = _controller.drive(_easeInTween); + _iconTurns = _controller.drive(_halfTween.chain(_easeInTween)); + _borderColor = _controller.drive(_borderColorTween.chain(_easeOutTween)); + _headerColor = _controller.drive(_headerColorTween.chain(_easeInTween)); + _iconColor = _controller.drive(_iconColorTween.chain(_easeInTween)); + _backgroundColor = + _controller.drive(_backgroundColorTween.chain(_easeOutTween)); + + _isExpanded = + PageStorage.of(context)?.readState(context) ?? widget.initiallyExpanded; + if (_isExpanded) _controller.value = 1.0; + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _handleTap() { + setState(() { + _isExpanded = !_isExpanded; + if (_isExpanded) { + _controller.forward(); + } else { + _controller.reverse().then((void value) { + if (!mounted) return; + setState(() { + // Rebuild without widget.children. + }); + }); + } + PageStorage.of(context)?.writeState(context, _isExpanded); + }); + if (widget.onExpansionChanged != null) + widget.onExpansionChanged(_isExpanded); + } + + Widget _buildChildren(BuildContext context, Widget child) { + final Color borderSideColor = _borderColor.value ?? Colors.transparent; + final Color titleColor = _headerColor.value; + + return Container( + decoration: BoxDecoration( + color: _backgroundColor.value ?? Colors.transparent, + border: Border( + top: BorderSide(color: borderSideColor), + bottom: BorderSide(color: borderSideColor), + )), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconTheme.merge( + data: IconThemeData(color: _iconColor.value), + child: Container( + color: widget.headerBackgroundColor ?? Colors.black, + child: ListTile( + onTap: _handleTap, + leading: widget.leading, + title: DefaultTextStyle( + style: Theme.of(context) + .textTheme + .subhead + .copyWith(color: titleColor), + child: widget.title, + ), + trailing: widget.trailing ?? + RotationTransition( + turns: _iconTurns, + child: Icon( + Icons.expand_more, + color: widget.iconColor ?? Colors.grey, + ), + ), + ), + ), + ), + ClipRect( + child: Align( + heightFactor: _heightFactor.value, + child: child, + ), + ), + ], + ), + ); + } + + @override + void didChangeDependencies() { + final ThemeData theme = Theme.of(context); + _borderColorTween..end = theme.dividerColor; + _headerColorTween + ..begin = theme.textTheme.subhead.color + ..end = theme.accentColor; + _iconColorTween + ..begin = theme.unselectedWidgetColor + ..end = theme.accentColor; + _backgroundColorTween..end = widget.backgroundColor; + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + final bool closed = !_isExpanded && _controller.isDismissed; + return AnimatedBuilder( + animation: _controller.view, + builder: _buildChildren, + child: closed ? null : Column(children: widget.children), + ); + } +} diff --git a/lib/components/headerComp.dart b/lib/components/headerComp.dart index 8d476a9..f756d18 100644 --- a/lib/components/headerComp.dart +++ b/lib/components/headerComp.dart @@ -8,8 +8,8 @@ class Index extends StatelessWidget { return Text( this.text, style: TextStyle( - color: Theme.of(context).primaryTextTheme.title.color - ), + //color: Theme.of(context).primaryTextTheme.title.color + ), ); } } diff --git a/lib/components/markdownComp.dart b/lib/components/markdownComp.dart index ae61332..a2e2508 100644 --- a/lib/components/markdownComp.dart +++ b/lib/components/markdownComp.dart @@ -2,6 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart' as md; import 'package:efox_flutter/utils/syntaxHighlighter.dart' show DartSyntaxHighlighter; +import 'package:efox_flutter/config/color.dart' show materialColor; +import 'package:efox_flutter/config/theme.dart' show AppTheme; +import 'package:efox_flutter/store/index.dart' show model; class Index extends StatelessWidget { final String data; diff --git a/lib/components/webviewComp.dart b/lib/components/webviewComp.dart index 03b1eeb..2f32559 100644 --- a/lib/components/webviewComp.dart +++ b/lib/components/webviewComp.dart @@ -21,6 +21,7 @@ class Index extends StatelessWidget { url: this.url, enableAppScheme: false, appBar: AppBar( + elevation: 0, title: Text( this.title, ), diff --git a/lib/components/widgetComp.dart b/lib/components/widgetComp.dart index c7009d4..e690299 100644 --- a/lib/components/widgetComp.dart +++ b/lib/components/widgetComp.dart @@ -84,14 +84,16 @@ class IndexState extends State { this.model = model; return Scaffold( appBar: AppBar( - title: Text(this.title), + //title: Text(this.title), elevation: 0, + backgroundColor: Color(AppTheme.secondColor), actions: this.getActions( context, ), leading: IconButton( icon: Icon(Icons.arrow_back), - color: Theme.of(context).primaryTextTheme.title.color, + //color: Theme.of(context).primaryTextTheme.title.color, + color: Color(AppTheme.blackColor), onPressed: () => Navigator.pop(context), ), ), @@ -130,7 +132,8 @@ class IndexState extends State { getActions(context) { return [ IconButton( - color: Theme.of(context).primaryTextTheme.title.color, + //color: Theme.of(context).primaryTextTheme.title.color, + color: Color(AppTheme.blackColor), icon: Icon( Icons.insert_link, ), @@ -151,7 +154,8 @@ class IndexState extends State { ), */ IconButton( icon: Icon(Icons.share), - color: Theme.of(context).primaryTextTheme.title.color, + //color: Theme.of(context).primaryTextTheme.title.color, + color: Color(AppTheme.blackColor), onPressed: () { final String msg = this.model.config.state.env.githubAssetOrigin + this.mdUrl; diff --git a/lib/config/color.dart b/lib/config/color.dart index ce6a6fd..1cbf1da 100644 --- a/lib/config/color.dart +++ b/lib/config/color.dart @@ -1,12 +1,26 @@ - Map materialColor = { - 'yellow':0xFFFFEB3B, - 'orange':0xFFFF9800, - 'amber':0xFFFFC107, - 'lime':0xFFCDDC39, - 'lightGreen':0xFF8BC34A, - 'red':0xFFF44336, - 'deepOrange':0xFFFF5722, - 'blue':0xFF2196F3, - 'pink':0xFFE91E63 + 'red': 0xFFF44336, + 'pink': 0xFFE91E63, + 'purple': 0xFF9C27B0, + 'deepPurple': 0xFF673AB7, + 'indigo': 0xFF3F51B5, + // + + 'blue': 0xFF2196F3, + 'lightBlue': 0xFF03A9F4, + 'cyan': 0xFF00BCD4, + 'teal': 0xFF009688, + 'green': 0xFF4CAF50, + // + 'lightGreen': 0xFF8BC34A, + 'lime': 0xFFCDDC39, + 'yellow': 0xFFFFEB3B, + 'amber': 0xFFFFC107, + 'orange': 0xFFFF9800, + // + 'deepOrange': 0xFFFF5722, + 'brown': 0xFF795548, + 'grey': 0xFF9E9E9E, + 'blueGrey': 0xFF607D8B, + 'black': 0xFF222222, }; diff --git a/lib/main.dart b/lib/main.dart index 1142e97..9eeeaa7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,22 +1,12 @@ import 'package:flutter/material.dart'; - -//语言包实例化 -import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; //语言包实例化 import 'package:efox_flutter/lang/index.dart' show AppLocalizationsDelegate, AppLocalizations; import 'package:efox_flutter/lang/config.dart' show ConfigLanguage; - -//引用Store 层 -import 'package:efox_flutter/store/index.dart' show model, Store; - -//路由 -import 'package:efox_flutter/router/index.dart' show FluroRouter; - -//主题 -import 'package:efox_flutter/config/theme.dart' show AppTheme; - -//统计 -import 'package:efox_flutter/utils/analytics.dart' as Analytics; +import 'package:efox_flutter/store/index.dart' show model, Store; //引用Store 层 +import 'package:efox_flutter/router/index.dart' show FluroRouter; //路由 +import 'package:efox_flutter/config/theme.dart' show AppTheme; //主题 +import 'package:efox_flutter/utils/analytics.dart' as Analytics; //统计 void main() => runApp(MainApp()); @@ -47,30 +37,30 @@ class MainAppState extends State { model: model, child: Store.connect(builder: (context, child, model) { return MaterialApp( - localeResolutionCallback: (deviceLocale, supportedLocales) { - print( - 'deviceLocale=$deviceLocale supportedLocales=$supportedLocales'); - Locale _locale = supportedLocales.contains(deviceLocale) - ? deviceLocale - : Locale('zh'); - return _locale; - }, - onGenerateTitle: (context) { - // 设置多语言代理 - AppLocalizations.setProxy(setState, _delegate); - return 'flutter'; - }, - localizationsDelegates: [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - _delegate, - ], - supportedLocales: ConfigLanguage.supportedLocales, + localeResolutionCallback: (deviceLocale, supportedLocales) { + print( + 'deviceLocale=$deviceLocale supportedLocales=$supportedLocales'); + Locale _locale = supportedLocales.contains(deviceLocale) + ? deviceLocale + : Locale('zh'); + return _locale; + }, + onGenerateTitle: (context) { + // 设置多语言代理 + AppLocalizations.setProxy(setState, _delegate); + return 'flutter'; + }, + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + _delegate, + ], + supportedLocales: ConfigLanguage.supportedLocales, // title: 'Flutter Demo', - theme: AppTheme.getThemeData(model.config.state.theme), - onGenerateRoute: FluroRouter.router.generator, - navigatorObservers: [Analytics.observer], - ); + theme: AppTheme.getThemeData(model.config.state.theme), + onGenerateRoute: FluroRouter.router.generator, + navigatorObservers: [Analytics.observer], + ); }), ); } diff --git a/lib/page/home.dart b/lib/page/home.dart index a3f1b61..cd9e83f 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -8,6 +8,8 @@ import 'package:efox_flutter/controller/index.dart' as Controller; import 'component/tabs.dart' as TabIndex; import 'mine/index_2.dart' as MyIndex; +import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; + class Index extends StatefulWidget { @override _IndexState createState() => new _IndexState(); @@ -22,6 +24,7 @@ class _IndexState extends State { super.initState(); _pageController = PageController(); Controller.initState(); + AppVersion().check(context); } @override diff --git a/lib/page/mine/index_2.dart b/lib/page/mine/index_2.dart index fcae11b..6814895 100644 --- a/lib/page/mine/index_2.dart +++ b/lib/page/mine/index_2.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; +import 'dart:io' show Platform; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; //import 'package:efox_flutter/router/index.dart' show FluroRouter; import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'package:efox_flutter/store/index.dart' show model; import 'package:efox_flutter/config/color.dart' show materialColor; +import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; class _IndexState extends State { @override @@ -47,8 +49,8 @@ class _IndexState extends State { @override Widget build(BuildContext context) { List _EdageList = []; - materialColor.forEach((k,v){ - _EdageList.add(this.Edage(k,v)); + materialColor.forEach((k, v) { + _EdageList.add(this.Edage(k, v)); }); return Scaffold( appBar: AppBar( @@ -75,23 +77,56 @@ class _IndexState extends State { ), ExpansionTile( leading: Icon(Icons.color_lens), - title: Text(AppLocalizations.$t('common_mine_1.theme')), + title: Row( + children: [ + Text(AppLocalizations.$t('common_mine_1.theme')), + Container( + margin: EdgeInsets.fromLTRB(5, 5, 0, 0), + child: Container( + color: Color(materialColor[model.config.state.theme]), + height: 15, + width: 15, + ), + ) + ], + ), children: [ - Wrap( - spacing: 10, - runSpacing: 5, - children: _EdageList, + Padding( + padding: EdgeInsets.all(10), + child: Wrap( + spacing: 10, + runSpacing: 5, + children: _EdageList, + ), ) ], ), Divider( color: Color(AppTheme.lineColor), ), + ListTile( + onTap: () { + AppVersion().check(context, showTips: true); + }, + leading: Icon(Icons.history), + title: Text(AppLocalizations.$t('common_mine_1.version')), + trailing: Container( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(model.config.state.appVersion), + Icon(Icons.navigate_next) + ], + ), + )), + Divider( + color: Color(AppTheme.lineColor), + ), ], )); } - Widget Edage(name,color) { + Widget Edage(name, color) { return GestureDetector( onTap: () { model.dispatch('config', 'setTheme', name); diff --git a/lib/store/models/config_state_model.dart b/lib/store/models/config_state_model.dart index 1f36f7d..4c2b9c0 100644 --- a/lib/store/models/config_state_model.dart +++ b/lib/store/models/config_state_model.dart @@ -3,12 +3,14 @@ import 'package:efox_flutter/config/index.dart' as Config; import 'package:efox_flutter/store/index.dart' show model; import 'package:efox_flutter/utils/loadAsset.dart' show loadAssets; import 'package:efox_flutter/utils/localstage.dart' show LocalStorage; +import 'package:package_info/package_info.dart' show PackageInfo; class ConfigInfo { bool isPro = Config.isPro; String version = '1.0'; dynamic env = Config.env; String theme = 'red'; + String appVersion = '-'; } ConfigInfo _appConfigInfo = new ConfigInfo(); @@ -16,6 +18,11 @@ ConfigInfo _appConfigInfo = new ConfigInfo(); class ConfigModel { get state => _appConfigInfo; + Future getAppVersion() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + _appConfigInfo.appVersion = await packageInfo.version; + } + Future getTheme() async { String theme = await LocalStorage.get('theme'); if (theme != null) { diff --git a/lib/store/models/main_state_model.dart b/lib/store/models/main_state_model.dart index 6032798..dc1ec67 100644 --- a/lib/store/models/main_state_model.dart +++ b/lib/store/models/main_state_model.dart @@ -21,6 +21,8 @@ class MainStateModel extends Model with UserModel { this.state = { 'config': config, }; + //init model data + config.getAppVersion(); // } diff --git a/lib/utils/appVersion.dart b/lib/utils/appVersion.dart new file mode 100644 index 0000000..676a6c2 --- /dev/null +++ b/lib/utils/appVersion.dart @@ -0,0 +1,124 @@ +import 'dart:io'; +import 'dart:convert'; +import 'package:package_info/package_info.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:flutter_downloader/flutter_downloader.dart'; +import 'package:open_file/open_file.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:efox_flutter/store/http.dart' as Http; +import 'package:flutter/material.dart'; + +class AppVersion { + var _context; + Future _checkPermission() async { + PermissionStatus permission = await PermissionHandler() + .checkPermissionStatus(PermissionGroup.storage); + if (permission != PermissionStatus.granted) { + Map permissions = + await PermissionHandler() + .requestPermissions([PermissionGroup.storage]); + if (permissions[PermissionGroup.storage] == PermissionStatus.granted) { + return true; + } + } else { + return true; + } + return false; + } + + // 获取安装地址 + Future get _apkLocalPath async { + final directory = await getExternalStorageDirectory(); + return directory.path; + } + + Future check(context, {showTips: false}) async { + _context = context; + if (!Platform.isAndroid) return; + // permission Status + bool _permissisonReady = await this._checkPermission(); + if (!_permissisonReady) return; + // + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + String version = packageInfo.version; + String platform = Platform.isAndroid ? 'android' : 'ios'; + print('version=$version $platform'); + Map d = await checkVersion(version, platform); + print(d); + if (d['isNew']) { + this._showDialog(context, d); + } else if (showTips) { + Scaffold.of(context).showSnackBar(new SnackBar( + content: new Text('已经是最新版本'), + )); + } + } + + void _showDialog(context, d) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('升级提示'), + content: Text('发现新版本 ${d['version']}'), + actions: [ + FlatButton( + child: Text('取消'), + onPressed: () { + Navigator.pop(context); + }, + ), + FlatButton( + textColor: Theme.of(context).primaryColor, + child: Text('确定'), + onPressed: () async { + await _downAndInstall(d['version']); + Navigator.pop(context); + }, + ) + ], + ); + }); + } + + Future checkVersion(String version, String platform) async { + var res = await Http.get( + 'https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/version.json'); + res = json.decode(res); + print('res=${res['version']}'); + String newVersion = (res['version'] != null) ? res['version'] : version; + //newVersion = '1.0.1'; //debug code + print('$newVersion $res $version'); + bool isNewestVersion = (newVersion == version) ? false : true; + Map d = { + 'version': newVersion, + 'isNew': isNewestVersion, + 'platform': platform + }; + return Future.value(d); + } + + Future _downAndInstall(String version) async { + String _finalApkPath = await _apkLocalPath; + String fileName = 'app-release.apk'; + final taskId = await FlutterDownloader.enqueue( + url: + 'https://github.com/efoxTeam/flutter-ui/releases/download/v$version/$fileName', + savedDir: _finalApkPath, + fileName: fileName, + showNotification: + true, // show download progress in status bar (for Android) + openFileFromNotification: + true, // click on notification to open downloaded file (for Android) + ); + await FlutterDownloader.loadTasks(); + FlutterDownloader.registerCallback((id, status, progress) { + print( + 'Download task ($id) is in status ($status) and process ($progress) status ${DownloadTaskStatus.complete} _finalApkPath=$_finalApkPath'); + if (taskId == id && status == DownloadTaskStatus.complete) { + OpenFile.open(_finalApkPath); + FlutterDownloader.open(taskId: id); + } + }); + } +} diff --git a/locale/en.json b/locale/en.json index 31dc737..46c2ff3 100644 --- a/locale/en.json +++ b/locale/en.json @@ -18,7 +18,8 @@ "environment": "Select Environment", "compProgress": "Components Progress", "success": "Success To Change ", - "theme": "Select Theme" + "theme": "Select Theme", + "version": "Version" }, "mine": { "loadNetwork": "Load Network Document Resources", diff --git a/locale/zh.json b/locale/zh.json index 4facbb4..8a782c6 100644 --- a/locale/zh.json +++ b/locale/zh.json @@ -18,7 +18,8 @@ "environment": "选择环境", "compProgress": "组件进度", "success": "切换成功", - "theme": "选择主题" + "theme": "选择主题", + "version": "版本" }, "mine": { "loadNetwork": "网络优良,可选择加载线上文档资源", diff --git a/pubspec.yaml b/pubspec.yaml index fb24490..44564e6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ description: A new Flutter project. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # Read more about versioning at semver.org. -version: 1.0.1+1 +version: 1.0.2+1 environment: sdk: ">=2.1.0 <3.0.0" @@ -35,6 +35,11 @@ dependencies: flutter_screenutil: ^0.5.1 firebase_analytics: ^2.0.2+1 random_pk: ^0.0.3 + device_info: ^0.4.0+1 + package_info: ^0.4.0+2 + flutter_downloader: ^1.1.6 + open_file: ^2.0.1+1 + permission_handler: ^3.0.0 dev_dependencies: flutter_test: From be2b3b419d1458177d441b64c2a3eaae87d3d496 Mon Sep 17 00:00:00 2001 From: KenZR Date: Fri, 22 Mar 2019 16:53:08 +0800 Subject: [PATCH 030/100] feat:v1.0.2 --- readme.md | 182 ++++---------------------------------- readme/apk.png | Bin 5975 -> 5980 bytes readme/background.md | 4 + readme/pr.md | 11 +++ readme/qq.md | 5 ++ readme/v1.0.0_preview.md | 5 ++ readme/widget_progress.md | 138 +++++++++++++++++++++++++++++ 7 files changed, 179 insertions(+), 166 deletions(-) create mode 100644 readme/background.md create mode 100644 readme/qq.md create mode 100644 readme/v1.0.0_preview.md create mode 100644 readme/widget_progress.md diff --git a/readme.md b/readme.md index 557e220..b3f25c3 100644 --- a/readme.md +++ b/readme.md @@ -1,178 +1,28 @@ -# Flutter UI +# Flutter UI v1.0.2 ![android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png](android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png) -> flutter ui 开发者社区 提供各种flutter相关开发教程 与 demo +> flutter ui 开发者社区 +## 功能清单 ++ widget 组件教程 ++ 多语言切换 ++ 多主题切换 ++ 自动更新检测 + +## apk 下载 +![安卓包下载](readme/apk.png) +[安卓包下载](https://github.com/efoxTeam/flutter-ui/releases/download/v1.0.2/app-release.apk) -## 项目背景 -* Google推出Flutter跨平台解决方案,渐渐地受到了开发者们的关注,结合dart使用,能够用一套代码实现开发iOS与Android两套应用,同时也是谷歌的下一代操作系统 Fuchsia 的开发框架,未来还将可以直接编译成桌面应用。 -* Flutter拥有丰富的组件库,多样化的主题与UI风格,让开发者更简单的上手完成界面交互,从而提高了开发效率。 -* 此套组件库在几次没有硝烟的研讨中,命名为"Flutter UI",使命为"知识千万点,学习第一条。代码快点敲,bug无数行"。没错,就是这么不押韵。 +## 项目相关 ++ [apk包历史版本](https://github.com/efoxTeam/flutter-ui/releases) ++ [组件开发进度](readme/widget_progress.md) ++ [贡献PR参考](readme/pr.md) -## Release Apk 安装包 -![apk download](readme/apk.png) -[历史版本](https://github.com/efoxTeam/flutter-ui/releases) -## 开发者如何参与完善控件 +## 项目交流 -欢迎有兴趣的小伙伴QQ扫描以上二维码参与进来,一同完善组件 -同时也可以把相关问题通过[issues](https://github.com/efoxTeam/flutter-ui/issues)方式与我们联系 -[开发者如何参与完善控件](readme/pr.md) -## 环境与构建 -* 自行完成flutter环境配置 -* 通过 fork 项目master分支代码,进入项目 -* 切换flutter到master分支,步骤如下(若已切换可跳过) - 1. flutter channel master // 选择master分支 - 2. flutter upgrade // 更新代码 -* 运行模拟器或真机 -* flutter run //运行程序 -* flutter build apk //生成apk -## app预览 - -![Alt 预览](readme/flutter_ui2.gif) -![Alt 预览](readme/flutter_ui3.gif) - - -## 目录 -``` -Flutter UI - ├─assets 静态资源 - ├─docs 教程文件 - ├─locale 语言包 - ├─lib dart执行代码 - ├─components - ├─config 配置文件 - ├─controller - ├─lang 多语言控制类 - ├─page 路由关联页面 - │ ├─component - │ └─mine - ├─router 路由 - ├─store 数据管理 - │ ├─models - │ └─objects - ├─utils 项目工具类 - └─widget 项目组件库 - ├─animate - │ ├─animatedbuilder 【✔️ v1.0】 - │ ├─animatedcontainer - │ ├─animatedcrossfade - │ ├─animateddefaulttextstyle - │ ├─animatedliststate - │ ├─animatedmodalbarrier - │ ├─animatedopacity - │ ├─animatedphysicalmodel - │ ├─animatedpositioned - │ ├─animatedsize - │ ├─animatedwidget - │ ├─animatedwidgetbasestate - │ ├─animationcontroller - │ ├─decoratedboxtransition - │ ├─fadetransition - │ ├─hero - │ ├─positionedtransition - │ ├─rotationtransition - │ ├─scaletransition - │ ├─sizetransition - │ └─slidetransition - ├─bulletbox - │ ├─alertdialog 【✔️ v1.0】 - │ ├─bottomsheet 【✔️ v1.0】 - │ ├─expansionPanel 【✔️ v1.0】 - │ ├─simpledialog 【✔️ v1.0】 - │ └─snackbar 【✔️ v1.0】 - ├─common - │ ├─assetbundle - │ ├─buttonbar - │ ├─chip - │ ├─container 【✔️ v1.0】 - │ ├─divider 【✔️ v1.0】 - │ ├─flatbutton 【✔️ v1.0】 - │ ├─icon 【✔️ v1.0】 - │ ├─iconbutton - │ ├─image - │ ├─listtile - │ ├─placeholder - │ ├─raisedbutton - │ ├─rawimage - │ ├─stepper - │ ├─text 【✔️ v1.0】 - │ └─tooltip - ├─form - │ ├─checkbox 【✔️ v1.0】 - │ ├─checkboxlisttile 【✔️ v1.0】 - │ ├─slider 【✔️ v1.0】 - │ ├─switch 【✔️ v1.0】 - │ ├─switchListTile 【✔️ v1.0】 - │ ├─daypicker 【✔️ v1.0】 - │ ├─radio 【✔️ v1.0】 - │ ├─radioListTile 【✔️ v1.0】 - │ ├─form - │ ├─formfield - │ ├─rawkeyboard - │ ├─textfield 【✔️ v1.0】 - │ └─textinput - ├─gestures - │ ├─absorbpointer - │ ├─dismissible - │ ├─dragtarget - │ ├─gesturedetector - │ ├─ignorepointer - │ └─longpressdraggable 【✔️ v1.0】 - ├─navigator - │ ├─appbar 【✔️ v1.0】 - │ ├─bottomnavigationbar 【✔️ v1.0】 - │ ├─drawer 【✔️ v1.0】 - │ ├─floatingactionbutton 【✔️ v1.0】 - │ ├─materialapp - │ ├─navigator - │ ├─popupmenubutton - │ ├─scaffold 【✔️ v1.0】 - │ ├─tabbar - │ ├─tabbarview - │ └─widgetsapp - ├─regular - │ ├─align 【✔️ v1.0】 - │ ├─aspectratio 【✔️ v1.0】 - │ ├─center 【✔️ v1.0】 - │ ├─column 【✔️ v1.0】 - │ ├─constrainedbox 【✔️ v1.0】 - │ ├─container 【✔️ v1.0】 - │ ├─fittedbox 【✔️ v1.0】 - │ ├─flow 【✔️ v1.0】 - │ ├─layoutbuilder 【✔️ v1.0】 - │ ├─listbody 【✔️ v1.0】 - │ ├─listview 【✔️ v1.0】 - │ ├─padding 【✔️ v1.0】 - │ ├─row 【✔️ v1.0】 - │ ├─stack 【✔️ v1.0】 - │ ├─table 【✔️ v1.0】 - │ └─wrap 【✔️ v1.0】 - ├─scrollview - │ ├─customscrollview 【✔️ v1.0】 - │ ├─gridview 【✔️ v1.0】 - │ ├─listview 【✔️ v1.0】 - │ ├─nestedscrollview 【✔️ v1.0】 - │ ├─scrollable 【✔️ v1.0】 - │ ├─scrollbar 【✔️ v1.0】 - │ ├─scrollcontroller 【✔️ v1.0】 - │ └─singlechildscrollview 【✔️ v1.0】 - └─vision - ├─backdropfilter - ├─clipoval - ├─clippath - ├─cliprect - ├─custompaint - ├─decoratedbox - ├─fractionaltranslation - ├─mediaquery - ├─opacity - ├─rotatedbox - ├─theme - └─transform -``` diff --git a/readme/apk.png b/readme/apk.png index da4584bc2e7b9c5fe89f937536ab47d963fd2191..2d9503fd9f6b2356f0e305bc21d3256d2dd58e6f 100644 GIT binary patch literal 5980 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 Wv4=XVjD@D=Ku_D~%o9y&=>Gwe9#eY& literal 5975 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 + +欢迎有兴趣的小伙伴QQ扫描以上二维码参与进来,一同完善组件 +同时也可以把相关问题通过[issues](https://github.com/efoxTeam/flutter-ui/issues)方式与我们联系 diff --git a/readme/v1.0.0_preview.md b/readme/v1.0.0_preview.md new file mode 100644 index 0000000..afc9e73 --- /dev/null +++ b/readme/v1.0.0_preview.md @@ -0,0 +1,5 @@ + +# app预览 + +![Alt 预览](flutter_ui2.gif) +![Alt 预览](flutter_ui3.gif) \ No newline at end of file diff --git a/readme/widget_progress.md b/readme/widget_progress.md new file mode 100644 index 0000000..a107eaa --- /dev/null +++ b/readme/widget_progress.md @@ -0,0 +1,138 @@ +# 组件开发进度 +``` +Flutter UI + ├─assets 静态资源 + ├─docs 教程文件 + ├─locale 语言包 + ├─lib dart执行代码 + ├─components + ├─config 配置文件 + ├─controller + ├─lang 多语言控制类 + ├─page 路由关联页面 + │ ├─component + │ └─mine + ├─router 路由 + ├─store 数据管理 + │ ├─models + │ └─objects + ├─utils 项目工具类 + └─widget 项目组件库 + ├─animate + │ ├─animatedbuilder 【✔️ v1.0】 + │ ├─animatedcontainer + │ ├─animatedcrossfade + │ ├─animateddefaulttextstyle + │ ├─animatedliststate + │ ├─animatedmodalbarrier + │ ├─animatedopacity + │ ├─animatedphysicalmodel + │ ├─animatedpositioned + │ ├─animatedsize + │ ├─animatedwidget + │ ├─animatedwidgetbasestate + │ ├─animationcontroller + │ ├─decoratedboxtransition + │ ├─fadetransition + │ ├─hero + │ ├─positionedtransition + │ ├─rotationtransition + │ ├─scaletransition + │ ├─sizetransition + │ └─slidetransition + ├─bulletbox + │ ├─alertdialog 【✔️ v1.0】 + │ ├─bottomsheet 【✔️ v1.0】 + │ ├─expansionPanel 【✔️ v1.0】 + │ ├─simpledialog 【✔️ v1.0】 + │ └─snackbar 【✔️ v1.0】 + ├─common + │ ├─assetbundle + │ ├─buttonbar + │ ├─chip + │ ├─container 【✔️ v1.0】 + │ ├─divider 【✔️ v1.0】 + │ ├─flatbutton 【✔️ v1.0】 + │ ├─icon 【✔️ v1.0】 + │ ├─iconbutton + │ ├─image + │ ├─listtile + │ ├─placeholder + │ ├─raisedbutton + │ ├─rawimage + │ ├─stepper + │ ├─text 【✔️ v1.0】 + │ └─tooltip + ├─form + │ ├─checkbox 【✔️ v1.0】 + │ ├─checkboxlisttile 【✔️ v1.0】 + │ ├─slider 【✔️ v1.0】 + │ ├─switch 【✔️ v1.0】 + │ ├─switchListTile 【✔️ v1.0】 + │ ├─daypicker 【✔️ v1.0】 + │ ├─radio 【✔️ v1.0】 + │ ├─radioListTile 【✔️ v1.0】 + │ ├─form + │ ├─formfield + │ ├─rawkeyboard + │ ├─textfield 【✔️ v1.0】 + │ └─textinput + ├─gestures + │ ├─absorbpointer + │ ├─dismissible + │ ├─dragtarget + │ ├─gesturedetector + │ ├─ignorepointer + │ └─longpressdraggable 【✔️ v1.0】 + ├─navigator + │ ├─appbar 【✔️ v1.0】 + │ ├─bottomnavigationbar 【✔️ v1.0】 + │ ├─drawer 【✔️ v1.0】 + │ ├─floatingactionbutton 【✔️ v1.0】 + │ ├─materialapp + │ ├─navigator + │ ├─popupmenubutton + │ ├─scaffold 【✔️ v1.0】 + │ ├─tabbar + │ ├─tabbarview + │ └─widgetsapp + ├─regular + │ ├─align 【✔️ v1.0】 + │ ├─aspectratio 【✔️ v1.0】 + │ ├─center 【✔️ v1.0】 + │ ├─column 【✔️ v1.0】 + │ ├─constrainedbox 【✔️ v1.0】 + │ ├─container 【✔️ v1.0】 + │ ├─fittedbox 【✔️ v1.0】 + │ ├─flow 【✔️ v1.0】 + │ ├─layoutbuilder 【✔️ v1.0】 + │ ├─listbody 【✔️ v1.0】 + │ ├─listview 【✔️ v1.0】 + │ ├─padding 【✔️ v1.0】 + │ ├─row 【✔️ v1.0】 + │ ├─stack 【✔️ v1.0】 + │ ├─table 【✔️ v1.0】 + │ └─wrap 【✔️ v1.0】 + ├─scrollview + │ ├─customscrollview 【✔️ v1.0】 + │ ├─gridview 【✔️ v1.0】 + │ ├─listview 【✔️ v1.0】 + │ ├─nestedscrollview 【✔️ v1.0】 + │ ├─scrollable 【✔️ v1.0】 + │ ├─scrollbar 【✔️ v1.0】 + │ ├─scrollcontroller 【✔️ v1.0】 + │ └─singlechildscrollview 【✔️ v1.0】 + └─vision + ├─backdropfilter + ├─clipoval + ├─clippath + ├─cliprect + ├─custompaint + ├─decoratedbox + ├─fractionaltranslation + ├─mediaquery + ├─opacity + ├─rotatedbox + ├─theme + └─transform +``` \ No newline at end of file From 298b2ce503ac2f848e5a7569c4f890bb11030e50 Mon Sep 17 00:00:00 2001 From: KenZR Date: Fri, 22 Mar 2019 16:57:23 +0800 Subject: [PATCH 031/100] =?UTF-8?q?doc:=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index b3f25c3..3709a97 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@ -# Flutter UI v1.0.2 -![android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png](android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png) +# Flutter UI v1.0.2 -> flutter ui 开发者社区 + +> flutter 开发者社区 ## 功能清单 + widget 组件教程 + 多语言切换 From fc7ad7677ac147ec96c406e79666ea71ed13a0fa Mon Sep 17 00:00:00 2001 From: KenZR Date: Fri, 22 Mar 2019 17:33:34 +0800 Subject: [PATCH 032/100] =?UTF-8?q?feat:=E5=B1=8F=E8=94=BDios=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E6=A3=80=E6=B5=8B=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/mine/index_2.dart | 40 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/lib/page/mine/index_2.dart b/lib/page/mine/index_2.dart index 6814895..047e56b 100644 --- a/lib/page/mine/index_2.dart +++ b/lib/page/mine/index_2.dart @@ -104,24 +104,28 @@ class _IndexState extends State { Divider( color: Color(AppTheme.lineColor), ), - ListTile( - onTap: () { - AppVersion().check(context, showTips: true); - }, - leading: Icon(Icons.history), - title: Text(AppLocalizations.$t('common_mine_1.version')), - trailing: Container( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(model.config.state.appVersion), - Icon(Icons.navigate_next) - ], - ), - )), - Divider( - color: Color(AppTheme.lineColor), - ), + (Platform.isAndroid) + ? ListTile( + onTap: () { + AppVersion().check(context, showTips: true); + }, + leading: Icon(Icons.history), + title: Text(AppLocalizations.$t('common_mine_1.version')), + trailing: Container( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(model.config.state.appVersion), + Icon(Icons.navigate_next) + ], + ), + )) + : Container(), + (Platform.isAndroid) + ? Divider( + color: Color(AppTheme.lineColor), + ) + : Container(), ], )); } From fe5b7dba121945acec8e20e19cc70817ef17bd7d Mon Sep 17 00:00:00 2001 From: KenZR Date: Fri, 22 Mar 2019 18:24:23 +0800 Subject: [PATCH 033/100] =?UTF-8?q?feat:=E8=87=AA=E5=AE=9A=E4=B9=89expansi?= =?UTF-8?q?onTile=20=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/components/expansionTile.dart | 4 ++-- lib/config/theme.dart | 15 ++++++--------- lib/page/mine/index_2.dart | 4 +++- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/components/expansionTile.dart b/lib/components/expansionTile.dart index 43ae1bc..21e3089 100644 --- a/lib/components/expansionTile.dart +++ b/lib/components/expansionTile.dart @@ -202,10 +202,10 @@ class _ExpansionTileState extends State _borderColorTween..end = theme.dividerColor; _headerColorTween ..begin = theme.textTheme.subhead.color - ..end = theme.accentColor; + ..end = theme.primaryColor; _iconColorTween ..begin = theme.unselectedWidgetColor - ..end = theme.accentColor; + ..end = theme.primaryColor; _backgroundColorTween..end = widget.backgroundColor; super.didChangeDependencies(); } diff --git a/lib/config/theme.dart b/lib/config/theme.dart index ea032a9..0582100 100644 --- a/lib/config/theme.dart +++ b/lib/config/theme.dart @@ -15,7 +15,7 @@ class AppTheme { static int blackColor = 0xFF000000; static int lineColor = 0xFFEEEEEE; static getThemeData(String theme) { - print('==================================getThemeData=$theme'); + //print('==================================getThemeData=$theme'); mainColor = materialColor[theme]; ThemeData themData = ThemeData( textTheme: TextTheme( @@ -36,14 +36,11 @@ class AppTheme { ), accentColor: Colors.grey, // 选中颜色 primaryColor: Color(mainColor), // appbar背景 - primaryTextTheme:TextTheme( - title:TextStyle( - // color: Colors.red - ), - button: TextStyle( - color: Colors.red - ) - ), + primaryTextTheme: TextTheme( + title: TextStyle( + // color: Colors.red + ), + button: TextStyle(color: Colors.red)), scaffoldBackgroundColor: Color(secondColor), // 整体的scaffold背景颜色 ); return themData; diff --git a/lib/page/mine/index_2.dart b/lib/page/mine/index_2.dart index 047e56b..ca75f0c 100644 --- a/lib/page/mine/index_2.dart +++ b/lib/page/mine/index_2.dart @@ -6,6 +6,7 @@ import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'package:efox_flutter/store/index.dart' show model; import 'package:efox_flutter/config/color.dart' show materialColor; import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; +import 'package:efox_flutter/components/expansionTile.dart' as Comp; class _IndexState extends State { @override @@ -75,8 +76,9 @@ class _IndexState extends State { Divider( color: Color(AppTheme.lineColor), ), - ExpansionTile( + Comp.ExpansionTile( leading: Icon(Icons.color_lens), + headerBackgroundColor: Colors.transparent, title: Row( children: [ Text(AppLocalizations.$t('common_mine_1.theme')), From a2a8269190d5be041e0fa4d6eb8cc6a7951de69b Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Fri, 22 Mar 2019 18:30:03 +0800 Subject: [PATCH 034/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0gesturedetector?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/widget/gestures/gesturedetector/index.md | 22 ++- lib/components/webviewComp.dart | 1 - lib/widget/gestures/gesturedetector/demo.dart | 178 +++++++++--------- .../gesturedetector/demo_force_press.dart | 12 +- .../gestures/gesturedetector/demo_pan.dart | 76 ++++---- .../gestures/gesturedetector/demo_scale.dart | 53 ++++++ .../gestures/gesturedetector/demo_tap.dart | 8 +- .../gestures/gesturedetector/index.dart | 2 + 8 files changed, 212 insertions(+), 140 deletions(-) create mode 100644 lib/widget/gestures/gesturedetector/demo_scale.dart diff --git a/docs/widget/gestures/gesturedetector/index.md b/docs/widget/gestures/gesturedetector/index.md index b13f8d0..043bdcd 100644 --- a/docs/widget/gestures/gesturedetector/index.md +++ b/docs/widget/gestures/gesturedetector/index.md @@ -45,20 +45,36 @@ GestureDetector({ ``` ### 属性介绍 -#### 点击事件可以Tap属性,执行顺序如下罗列 +> 点击事件可用Tap属性,执行顺序如下罗列 - onTapDown: 触摸时触发 - onTapUp: 触摸离开时触发 - onTap: 点击后触发 - onTapCancel: 触发时取消 - onDoubleTap: 200毫秒内触摸时触发,但不触发onTap -#### 拖动事件可用Pan属性,执行顺序如下罗列,拖动时返回是手势位移数据 +> 拖动事件可用Pan属性,执行顺序如下罗列,拖动时返回是手势位移数据 - onPanDown: - onPanStart: (DragStartDetails e) {} 返回相对屏幕位置 - onPanUpdate: (DragUpdateDetails e) {} 回调函数中返回是手势位移数据 - onPanEnd: (DragEndDetails e) {} 回调函数中返回Velocity,手势瞬时速度,可结合动画使用 - onPanCancel: 回调取消 -#### 以下也可执行拖动事件,使用后不会触发Tap/Pan事件,执行顺序如下罗列,拖动时返回相对整个屏幕距离数据 +> 以下也可执行拖动事件,使用后不会触发Tap/Pan事件,执行顺序如下罗列,拖动时返回相对整个屏幕距离数据 - onForcePressStart: (ForcePressDetails e) {} - onForcePressPeak: (ForcePressDetails e) {} - onForcePressUpdate: (ForcePressDetails e) {} - onForcePressEnd: (ForcePressDetails e) {} +> 监听垂直或水平方向 +- onVerticalDragDown +- onVerticalDragStart +- onVerticalDragUpdate +- onVerticalDragEnd +- onVerticalDragCancel +- onHorizontalDragDown +- onHorizontalDragStart +- onHorizontalDragUpdate +- onHorizontalDragEnd +- onHorizontalDragCancel +> 手势放大 +- onScaleStart +- onScaleUpdate +- onScaleEnd + diff --git a/lib/components/webviewComp.dart b/lib/components/webviewComp.dart index 03b1eeb..3e6ee9f 100644 --- a/lib/components/webviewComp.dart +++ b/lib/components/webviewComp.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'headerComp.dart' as Header; import 'package:flutter_webview_plugin/flutter_webview_plugin.dart' show FlutterWebviewPlugin, WebviewScaffold; diff --git a/lib/widget/gestures/gesturedetector/demo.dart b/lib/widget/gestures/gesturedetector/demo.dart index e39ee81..0e72d49 100644 --- a/lib/widget/gestures/gesturedetector/demo.dart +++ b/lib/widget/gestures/gesturedetector/demo.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:random_pk/random_pk.dart' show RandomContainer; - class Index extends StatefulWidget { @override State createState() => _IndexState(); @@ -32,106 +30,98 @@ class _IndexState extends State { return Scaffold( appBar: AppBar( title: Text('GestureDetector'), + automaticallyImplyLeading: false, ), body: Center( - child: RandomContainer( - child: Column( - children: [ - GestureDetector( - onTap: () { - Scaffold.of(context).showSnackBar( - SnackBar(content: Text('you click the button'))); - }, - child: Icon( - Icons.share, - color: Colors.red, - ), - ), - Divider( - height: 20, + child: Column( + children: [ + GestureDetector( + onTap: () { + Scaffold.of(context).showSnackBar( + SnackBar(content: Text('you click the button'))); + }, + child: Icon( + Icons.share, + color: Colors.red, ), - GestureDetector( - onDoubleTap: () { - updateText('onDoubleTap'); - }, - onTapDown: (TapDownDetails e) { - updateText('onTapDown'); - print(e.globalPosition); - }, - onTapCancel: () { - updateText('onTapCancel'); - }, - // 连接点击两次的话,不会触发onTap,只会触发 onDoubleTap - onTap: () { - updateText('onTap'); - setState(() { - isOn = !isOn; - }); - }, - child: Container( - alignment: Alignment.center, - height: 100, - width: 200, - color: Colors.blue, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('TURN LIGHTS ON'), - Divider(), - Icon( - Icons.lightbulb_outline, - color: isOn ? Colors.yellow : Colors.grey, - ) - ], + ), + Divider( + height: 20, + ), + GestureDetector( + onDoubleTap: () { + updateText('onDoubleTap'); + }, + onTapDown: (TapDownDetails e) { + updateText('onTapDown'); + print(e.globalPosition); + }, + onTapCancel: () { + updateText('onTapCancel'); + }, + // 连接点击两次的话,不会触发onTap,只会触发 onDoubleTap + onTap: () { + updateText('onTap'); + setState(() { + isOn = !isOn; + }); + }, + child: Container( + margin: EdgeInsets.all(10), + color: Colors.red, + child: ListTile( + leading: Icon( + Icons.lightbulb_outline, + color: isOn ? Colors.yellow : Colors.grey, ), + title: Text("Click Here To Change Light"), ), ), - Text(_value), - Divider( - height: 20, - ), - Text("使用ForcePress相关属性将不会触发Tap属性"), - GestureDetector( - onForcePressEnd: (ev) { - updateText2('onForcePressEnd ${ev.globalPosition}'); - }, - onForcePressStart: (ev) { - updateText2('onForcePressStart ${ev.globalPosition}'); - }, - onForcePressUpdate: (ev) { - updateText2('onForcePressUpdate ${ev.globalPosition}'); - }, - onForcePressPeak: (ev) { - updateText2('onForcePressPeak ${ev.globalPosition}'); - }, - // 连接点击两次的话,不会触发onTap,只会触发 onDoubleTap - onTap: () { - updateText2('onTap'); - setState(() { - isOn = !isOn; - }); - }, - child: Container( - alignment: Alignment.center, - height: 100, - width: 200, - color: Colors.blue, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('Tap or DoubleTap is not useful'), - Divider(), - Icon( - Icons.lightbulb_outline, - color: isOn ? Colors.yellow : Colors.grey, - ) - ], - ), + ), + Text('Event: $_value'), + Text('Try to Double Click'), + Divider( + height: 40, + ), + Text("使用ForcePress相关属性将不会触发Tap属性"), + GestureDetector( + onForcePressEnd: (ev) { + updateText2('onForcePressEnd ${ev.globalPosition}'); + }, + onForcePressStart: (ev) { + updateText2('onForcePressStart ${ev.globalPosition}'); + }, + onForcePressUpdate: (ev) { + updateText2('onForcePressUpdate ${ev.globalPosition}'); + }, + onForcePressPeak: (ev) { + updateText2('onForcePressPeak ${ev.globalPosition}'); + }, + // 连接点击两次的话,不会触发onTap,只会触发 onDoubleTap + onTap: () { + updateText2('onTap'); + setState(() { + isOn = !isOn; + }); + }, + child: Container( + margin: EdgeInsets.all(10), + alignment: Alignment.center, + height: 50, + color: Colors.red, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Tap or Double Tap is not useful'), + ], ), ), - Text(_value2), - ], - ), + ), + Text(_value2), + Divider( + height: 20, + ), + ], ), ), ); diff --git a/lib/widget/gestures/gesturedetector/demo_force_press.dart b/lib/widget/gestures/gesturedetector/demo_force_press.dart index ec54449..3796dec 100644 --- a/lib/widget/gestures/gesturedetector/demo_force_press.dart +++ b/lib/widget/gestures/gesturedetector/demo_force_press.dart @@ -36,6 +36,7 @@ class _IndexState extends State { ), body: Center( child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ Text("使用ForcePress相关属性将不会触发Tap属性"), GestureDetector( @@ -63,14 +64,21 @@ class _IndexState extends State { ), ), ), + Divider( + height: 10, + ), Text(_value1), Divider( height: 20, ), - Text("也可以监听水平或垂直滚动, 以下是水平滚动,垂直不变化为0"), + Text("监听水平或垂直滚动"), + Text('以下是水平滚动,垂直不变化为0'), + Divider( + height: 20, + ), SizedBox( height: 100, - width: 100, + width: 200, child: GestureDetector( onHorizontalDragDown: (DragDownDetails e) { updateText2('onHorizontalDragDown $e ${e.globalPosition}'); diff --git a/lib/widget/gestures/gesturedetector/demo_pan.dart b/lib/widget/gestures/gesturedetector/demo_pan.dart index a069cc2..bf85e9d 100644 --- a/lib/widget/gestures/gesturedetector/demo_pan.dart +++ b/lib/widget/gestures/gesturedetector/demo_pan.dart @@ -27,43 +27,47 @@ class _IndexState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text('GestureDetectorDrag'), - ), - body: Center( - child: Stack( - children: [ - Positioned( - width:40, - height: 40, - top: _top, - left: _left, - child: GestureDetector( - child: CircleAvatar( - child: Text("Drag"), + appBar: AppBar( + title: Text('GestureDetectorDrag'), + automaticallyImplyLeading: false, + ), + body: ConstrainedBox( + // 占位撑开全屏 + constraints: BoxConstraints.expand(), + child: Stack( + alignment: Alignment.center, + children: [ + Text('Top: $_top, Left: $_left'), + Positioned( + width: 80, + height: 80, + top: _top, + left: _left, + child: GestureDetector( + child: CircleAvatar( + child: Text("Drag"), + ), + onPanStart: (DragStartDetails ev) { + print('onPanStart $ev'); + }, + // DragEndDetails结束时用户滑动的瞬间速度 + onPanEnd: (DragEndDetails ev) { + print('end $ev'); + }, + onPanCancel: () { + setPanEvent('onPanCancel'); + }, + // DragDownDetails返回相对屏幕的位置 + onPanDown: (DragDownDetails ev) { + print('DragDownDetails ${ev.globalPosition}'); + }, + onPanUpdate: (DragUpdateDetails ev) { + setPanEvent('onPanUpdate', ev); + }, ), - onPanStart: (DragStartDetails ev) { - print('onPanStart $ev'); - }, - // DragEndDetails结束时用户滑动的瞬间速度 - onPanEnd: (DragEndDetails ev) { - print('end $ev'); - }, - onPanCancel: () { - setPanEvent('onPanCancel'); - }, - // DragDownDetails返回相对屏幕的位置 - onPanDown: (DragDownDetails ev) { - print('DragDownDetails ${ev.globalPosition}'); - }, - onPanUpdate: (DragUpdateDetails ev) { - setPanEvent('onPanUpdate', ev); - }, ), - ), - ], - ), - ), - ); + ], + ), + )); } } diff --git a/lib/widget/gestures/gesturedetector/demo_scale.dart b/lib/widget/gestures/gesturedetector/demo_scale.dart new file mode 100644 index 0000000..9b8843b --- /dev/null +++ b/lib/widget/gestures/gesturedetector/demo_scale.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:random_pk/random_pk.dart' show RandomContainer; + +class Index extends StatefulWidget { + @override + State createState() => _IndexState(); +} + +class _IndexState extends State { + double _width = 100; + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('GestureDetector'), + automaticallyImplyLeading: false, + ), + body: Center( + child: RandomContainer( + changeOnRedraw: true, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("手势操作图片放大或缩小"), + Divider( + height: 10, + ), + GestureDetector( + child: Image.network( + 'https://avatars3.githubusercontent.com/u/15372930?s=460&v=4', + fit: BoxFit.contain, + width: _width, + ), + onScaleStart: (ScaleStartDetails ev) { + print('ScaleStartDetails $ev'); + }, + onScaleUpdate: (ScaleUpdateDetails ev) { + print("$ev"); + setState(() { + _width = 200 * ev.scale.clamp(.8, 10.0); + }); + }, + onScaleEnd: (ScaleEndDetails ev) { + print('ScaleEndDetails $ev'); + }, + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/widget/gestures/gesturedetector/demo_tap.dart b/lib/widget/gestures/gesturedetector/demo_tap.dart index 86b62dc..7173768 100644 --- a/lib/widget/gestures/gesturedetector/demo_tap.dart +++ b/lib/widget/gestures/gesturedetector/demo_tap.dart @@ -27,6 +27,7 @@ class _IndexState extends State { ), body: Center( child: Column( + mainAxisAlignment:MainAxisAlignment.center, children: [ Text("点击时,会先触发Tap事件,再触发Pan事件"), Text("触发外层滚动时,会触发onPanCancel事件"), @@ -59,15 +60,14 @@ class _IndexState extends State { }); }, child: Container( + margin: EdgeInsets.all(10), + color: Colors.red, alignment: Alignment.center, - height: 100, - width: 200, - color: Colors.blue, + height: 50, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('TURN LIGHTS ON'), - Divider(), Icon( Icons.lightbulb_outline, color: isOn ? Colors.yellow : Colors.grey, diff --git a/lib/widget/gestures/gesturedetector/index.dart b/lib/widget/gestures/gesturedetector/index.dart index ccb32fd..7e27b43 100644 --- a/lib/widget/gestures/gesturedetector/index.dart +++ b/lib/widget/gestures/gesturedetector/index.dart @@ -4,6 +4,7 @@ import 'demo.dart' as Demo; import 'demo_tap.dart' as DemoTap; import 'demo_pan.dart' as DemoPanDrag; import 'demo_force_press.dart' as DemoForcePress; +import 'demo_scale.dart' as DemoScale; class Index extends StatefulWidget { static String title = 'GestureDetector'; @@ -26,6 +27,7 @@ class _IndexState extends State { DemoTap.Index(), DemoPanDrag.Index(), DemoForcePress.Index(), + DemoScale.Index(), ], ); } From 03dfa24165070463d74a27e9001e87d5a16c0c0e Mon Sep 17 00:00:00 2001 From: KenZR Date: Sat, 23 Mar 2019 15:39:53 +0800 Subject: [PATCH 035/100] =?UTF-8?q?doc:=E5=A2=9E=E5=8A=A0demo=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 11 ++++++++++- readme/1.0.2/1.jpg | Bin 0 -> 57787 bytes readme/1.0.2/2.jpg | Bin 0 -> 57190 bytes readme/1.0.2/3.jpg | Bin 0 -> 54650 bytes readme/1.0.2/4.jpg | Bin 0 -> 60151 bytes readme/1.0.2/5.jpg | Bin 0 -> 44740 bytes readme/1.0.2/6.jpg | Bin 0 -> 100947 bytes 7 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 readme/1.0.2/1.jpg create mode 100644 readme/1.0.2/2.jpg create mode 100644 readme/1.0.2/3.jpg create mode 100644 readme/1.0.2/4.jpg create mode 100644 readme/1.0.2/5.jpg create mode 100644 readme/1.0.2/6.jpg diff --git a/readme.md b/readme.md index 3709a97..d48e23e 100644 --- a/readme.md +++ b/readme.md @@ -7,11 +7,20 @@ + 多语言切换 + 多主题切换 + 自动更新检测 ++ firebase 崩溃监控 ## apk 下载 ![安卓包下载](readme/apk.png) [安卓包下载](https://github.com/efoxTeam/flutter-ui/releases/download/v1.0.2/app-release.apk) +## Demo 预览 + + + + + + + ## 项目相关 + [apk包历史版本](https://github.com/efoxTeam/flutter-ui/releases) + [组件开发进度](readme/widget_progress.md) @@ -19,7 +28,7 @@ ## 项目交流 - + diff --git a/readme/1.0.2/1.jpg b/readme/1.0.2/1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4ff83ed4de22ba140ceb3e2daa615629e801537f GIT binary patch literal 57787 zcmeFZ1z45M);GQh0R@$AknZk~lGi%nK^;>g?ew+EW1VELMkd^?zzyJU+&I8DYk|t;DAl=84rZLs0`-?tm(uop#qJxfEdtk8H3e{11vv6< zZ7fb>gES{o_|NMe5YSCr*euLfuWuYpPjGy?X9)n{QKN*ivGjG4|KBY(v1b1H`u;n6 zV9KW!v7DP4OgXD+8FT7r#!|6whKI{a1H?d7^wd=q>~9EoEa_j$=uOECtQD&F#hav{ zB$)JS`z8!Mu3c8+tW-V@+`G4IvyD~*GeJ{e9uvdcorMwfN-j4wyi}v2i7Mcb0DAa@%jcEt@dUkWqXjSMKFY!ktF+UcQdcU43ZqvR z@6{?9sYpJ0%Awb*_uk5=r?0zyNhKSq`uJN44Xc^)WCAzWxT!d=$tfldt+BGU7nWEP z&{+joX04B=3qhT_UOBoh<tg7r=002OSKZ?QC;RA6V-^>9@lQ5Cg;%x6^ z86oG^CY=orUs3J3`F5=i@N;2lIp)HR9U*PGvsSZZuSXu5R@n{<&gODj576)5YEcIQ znzc_OFV`uwzI<7i0Cw8x$^U?7|8!g)LJnQt{g#?0YQgYy!4z;_GQoD4X_^;kP!@Dj zPegb;gAQGVF|DM6RL3)UAJ_Xe&IPVyj_p=zEH^8Fna_jFDrg;8*MhX|x-V8oFr7zY zGO-|OBmltOP6zH0g{PsjV*B@Y0C&Xza6A~k&~*V298m#yu{Sk<8T1D?xUJ#E!tvk@ zi30K7K$|c$FMudZw{!rz?=Tz>`p|8I{dxR%;(zH1pWcbT{{^v(SY*35(*0nKxY_OR z8=`_8FE~myib@>1)EUJ8XIx&r?y(4EA`MXZ{v9b4a^oexCloUT;T}7a-$~H= z|4mWY+QGLf-Z`&0pJs+&70orRxOq^jaUMU>6TVRj22U*VrUU>U$aOcSR`3&=r}q+X zhIK%35SqwoqHjw8iM)u7uDjA|vTHq>z#h!|9vk7C4w%qww!5o5Y`07>AHPy3juegC zkM~fwt_}_}j#_Qx)@8l+8~qfpKyt$Z%NBH70swAaEh?RDG0D@)R#QkdUbX4-;5n|9 z8y)~!tn_UOuycjHHyV8P4m^oJ?~*!+?4*}zOHOv~d((Sg+nL~(Vd36hD6(fL=B!Rb z%oJMG@dXP}2p+U6ICnI}$=kW7uosUn}6myCk*8KJs$kHM)B2dW=CFXw`G0 z=N}eCOqvx)bmwbmIwsun(@&oc?wOgnUoU*a0}n>OEdltacH)I)p6l_t@%#RaU&>Ua z#!kcp?y-$0-mn2I=kwJHhgIR>JKlEws|`l@ z6<$j2mJ5niphbyytf_cEnm>y_%=MG<$2xvd{wK)dpEua_f2_*h8ikl?r--BvB*o9U zs>Ey8pQcQv1suDD*A}PTDEz}QEf!U*Ii9ZZEiB}ua{v?O3-*vKmbduXgAcn0$ul9Q zz1%nc`(yQ-JK2222(q)$5BIE+?v*DOg?~(;IcW^SH%<)*nAI_(EkS z>wwiaz7DPWb*oEajq3N)#!rH3#H_a&20CB&%FAy8`=_6W1QKFJH}DpJD!UQ-vD5FR zSf&`NS*cp>4MRgmR_f=CatODqOAMduDP~%{ju`uW(|J9?H&B5e8^!Jn6uGmROKGfY zVmC+-&L8aYB7>)Iccp!_*ICYV1+q|t_)u(jm;k8{D)ZZ>fy)SgXWlF9c>KTy8!qED z&m~;4b~iZm6|<7V36%3EEFp3e<(GZ>MA%&W66=(-cZTpQ9M&m$Ozx8uBH%o14M^2D1T{0dw@6F4?+8HPo(_Ruk)s z-Zn1`ZrA`4K#JQEru{=H<3@t@MFEaclnX7x1=p~LMC#H_-yf3^tYS#isX(CGQd$yn z+0|-`SKSK#&9V>?-5DjC)wMX6vh?opGi6UOes_eQn*lDO0t4D_dD}0zxAqVHDLEJ) z)8iB(IF7U4DvmyIpUg zF<+a|u(w)}&8=#lfp+K^S9D_(CpJiisn2sA?&EaX90uRxiz$?+#H$QRk)($@|KVc3A z_*70O&X2K>aUjhKmjB%Sfz$wHw?H><>HFG={Md5Msi!;iF{?}s?hOOmmfIL1E@{0& zip;>l(Bu0x+)KA{?t$RI7?D71*NF{w4GtCpqvh_sa(gF*h1boKuPdGeuI!FZnCU<) z{Z?G8X8X*DJl>*~RShuB)Ca+2tzSxz9v8JFQY)Y2N;> zxRR}8o7)%4{L{4&!meIvE25c>lN-mpPElC;rT#74!(=ZMRdATSqO3~E?rC{a(2LRV z_8oXICi^Gbj}2{Skrwr~6A$|_HX*{E-Zkt>`bugaxxJ#Ds`FvjA~OEC?xK7-R0eu> zFzv~#yNL^hHHADE%MQwW!kY2CQu~)d3psHO6oGXxV4F3lSL;JkFUv+qS-y|>6J-Ga`_YRqrw`$8YDGA|cl|uUe{cOA4H3sS zu*SLfk%?CDRN++P@%`^d|DWeI==L3XGRnSrR3`R#d-NgyCr5fy?C)XhUk(ev=icuw zDmK5c-)&6=*8!w%r~Mgp05C7raCE%|>K0Y&@ExVJzpvpBgZ-iDZ?p%31!STIw_U&e z2ySJv`trqh4!(=P{EV@Gi~zlF`w@5lp#5R~pR_+=`VR#ioyJm%xzw?l81H0B!dP#t zGV;{grFq4%Q!=F?T0zmLY|gnT*BSaG22m!jdQs{}=@F}#7w^-D zig%Wim`a1AT{zQT3F%YY>pqpYR2ya^THwm@4%ar0pDLXA%{zYn1m8Udrp%!9bL4nd zwFqlCG7OPNKuKn=VC331;o$;)ZJ_xUzAGa3*G?7lUTSFI_z7YNB@ldfNoFyBSI<{m zo8gR?e!Tnvxud#C6BSwynYqVE;bYqQ++H4v|2^`uy%~lh`8C~N zmA@?Tmj(W^z+V>l%L0E{;4cgOWr4pe@RtStvcUhB7C`d-k7h@)2zozSo}zGnS6d1C z#ln9n@p_Kr9`U#%H_FzxNVMip%fAl}KC=akr~c_EFz8uwB>@*6zJJ;a0IVMg*1cjX zL~cowo$jg9nC-K z3p(wRXK)I3f67nqe0o5%2Qv$tF0cGkhW`^HTmW^cmX*;NUQVN!4*pcdtn`9Q$lrfk z@O@y8lvMukfUxC}m}h@JlZwuSip|wxGw|7h%Hmt|re&JiB7D6rjZ6XFH-9EUnC7_w zQ$4f}Czmzlpfxr7XFLD^{SKPto^~_nxVFiWRo56#-pV?QgzEL5F+<{UR5=mWzbPIQhAu(7VYQEsBmce+%E?+uO>-muf0TrvA1QL70$|`^;qD;9 z!@|M=;GpBcLJ9B~Xy}+&q{1Rfs3a_G>>LJn$jDha#VPDWpZJPJEKf?d>y($iAj^f>oMivOB=cE#}&Ly zyd?RatMsOXj64tG%PJo>0s{9`n;vI_>2h(<;Y`!AIza7dA8>$y^8<9RwMO8iFGa;@ z1CzGug3aunZ8(w<#$kDvP#1dp1c-fsL{mjcBK4dohPJ%eV?t}rN(|$|%kHd1Gmgd{ z$e2|-6&ph1lwJiAz}&@rN|*qwRS9KJS&v!(tO74&vfeFONT*c7VOzn3iG`XN+$TVl zMeRX*{3>0tpQly|aXE`doIq6~J)1Rya(D?FDFer~d%O?dH&P?DS60c$D+;&S2EaZ- z<>(m`$RZqcgxVj^qnpZNZUjV~9~yX_JaQp)=S90gxdsC^^8c)g&_w~vkR%?A356X` zfT+%Ha;^-xEG+i zxI8WZQ!uXCfo6rKG`wcKsHW_5GzZs(WNMen&1EW+(U_VnP?)w5S_)QVOb}Zrdz<^g z`Ur`nivjIDx%yVC!@OikbBjI-K4V8#J7y$8&HE5?mhSgdnh?dy0rC3Qj0~5+pveJ}2j5Me64e4K-ovpvEO5CE2_r9<>_Zrf-~hJ9@<0o>-y-3nySou?&}65K*CJPJUk0$AU7mI z6A)e-L{H10mr#_KKkZfA9@%~dOo26W@OymZp81LPjJ);%-hGveh|7In!U_AAyaX}z z?~OBbU^GhI@;E(yD8FRKa^c_2T|P(dMl+s;{D=Q zqb?!gR1Nm~iK+S(3lc0eM@f?D+=vHvsxa%L`k2A3bKW~}=_;wEzG&m9q0)z{EG-T9 z3MEAt!X5T-4QYll(D%ENAP*lWJ>?CTN=jX0=q2qBdeE#16gIKXmW!t(<>!Jy<;0 zyN);WzF%jDyHq7)EQowu;XS0=tnTW)p#K2pWIkjy>BBdG^3pb<24DNsgL3DW#Y6^w z(s+0sIM!EP$;c2QOZNX}CE97vi#emG;keZYe`D;+l~so9#zE;%5+FW{)>8iW{DjMO z$?dq+i=ExL{TpD`i1V>9>$q>qOqc=#n9b{VII88R_LgFhdyFG^@2RS1Gyj)!#Y;8C9O;j0g2jSSZiYAfXl|J&W+zRtso$9@ z1vO648O1_?WD`;~=u}_r9&Ri@a!QU=9->0+8*RbxGP-I8ewZyWZ!cx>;vQsI1b)Rl2=&t#zaY2KQe}QJ>t=RG`nBInmBFD z-AMXFGHPmzu;k<5RNb|kAJY>}j6&zhmvc!|XKSo-P`mb>+k)M&-Eq_NLi@{wt6;)N zPFqIaztg;OWJRzXaP|3M-iM$YrOG-UQgN>yM@yJ|so^|YiZ#Mm>LIl)-I9)&9NCe? zsQ3JfjpZmLuF4E-Pwh768bo^dmJ%jTqNsr}q2OyDVi?O9dmM9e8ZfUPMfhtlPcV11 z<_jwPNb1+t=J^=_)r@Fin)hxLNUuQmr15!X4@?t@9jO4Y!^Dq-(A(xuSrH(bqEwSF zqUBynbEy|yInYganLb;2-X^Y!0+d6~jZ3#MG0%cEMlSK`+E}jQs2L{PMFFE64xv#M zMPN_?-0NpkyNst`;V>TUY@B6_!WG@Y3w@a?TBk`s^?o8fyV6f?QY59*M<`TJKkt&) zv=*pZ9j*G3$TIh2*(%PI!T9*IxleFy@u5lw_R7l-j}JI4K+2J$*IImR-vD@ZoVMp} z^2rQ9}GuWH(qPMre|#w69G3Bivg)tP5CRnGb) zPs6xBFb9ov#v`zesNn}2Bo(w#jKN9ch4|mcWl@p-Vx`1c_!+8jl>{4g(epUGyQ(a# zUedE^`E_@w3y8!RfsASlQNWZmWPvo~d&$osW5jWqD6|;|5Y*}Iw zUvSrG`1$pyKO0&x%k}&GcE$0BuFhp|;l(fmB;~~~72GX0;V6YB1kyBkmAgp>2@)$n z4>f?-rUFHlRxgw8#>1lnfl41r8*&BVhVYWnV!QwpR}0dKl7@?M<{|pgx=zgB&rxC24+5)0j=R(4U6aLxXHK^sPIHD zF;FVS(8D7!*x3om9P%@fLb^6q ztYgApG#S0?V(H}|WjSxY;x!Bhjok}p2?YC5%1aYdF&MzUK_D~Ef(NjM<+cJ@Whsnl zEMQQC(Xk$zxU)FTC-Wq8LkF&Yz9RJRdBh*?bKZ9KN8`_2>s*u+%L(h9x`^(b+k(Bq zPUjql>ThUwS?zS7PI)d9*f$x3^;^b-T_Suai13=Qrz`mPoQlN5GiURY{4t~=WAg+sFtzsZuw5?7A|5v1lM#)5y(;am|fHpV`T zbf`)R*ZhH#w}9!J+WCCr&9E(8ev~DJRuDEs{Cfq9H)>z!9E0I`p;pe#pS(&)5*TS? zSO*!3IcSvTSoxFnxA`^bIXerK6$hKrmh-pFLDt#sxFqKo9t--BRaB699~g#X!!GHM zbjq!}VrV3HO|r*(Kg@tr373;MvbrHmQB`otM%)>B9=Bk81Li+aJ;04H+ZA^?c39!g z)fFl3ix&LfGrEy2Vg{@jS((J|*{|!I$rHJXUpwqqhTjcgK^B6&_C|l!H%nfCb6q*Nb`3?eaW_cvgQB_ulG;qpFO!$9)zT*5a@dk;al-83@YJn&Hp zW3h2Ccz2}mR4*-`+B|8E582(xy^1+=I|EzQ%xc2~trR*$C_4laOuh&giKR>=Gb}`W zmTe&sBugXqRuwOs-+rw7v>#Q5BiyAR8+W#Fbw~s3-<6qp3{E9eR6-{O(1{4k8pZ@R&mWY zMum3aePKx{cly!Q)B-OzFFWS3m>ispg#9R8nI-XX#GKLvM}|opd^w}%rYaIIsdc*NDxH11HCqqpCT_@)KcmID8@<)lO6UvnWsG~oFRik;3bh96WH`4@@CXe)i=&zzAhBcJCXY4R+h|0q8C`etutZV{_q3+7PNO z>#EziFC(K>V3Y72%*xwXk9R*Sv~bl#f(-wSu#xfC)a%o-;S+A*%u z)0qGHGIaeGFl(QhuK;nuW^rYwf*;}V5W_ZBWDy)kRPck95-n~cF(G7GL(jip^H`Giv~JA^BoopkJ$&U7)~wH2{g+x5V~x3@ z1^F{vq!CCbU*H&-3mo{6u~iLZ#e$KL9^g?U4eg4YB)0S3m7MIv`1UFOL0C zU;g|fyLpGQ7-l?-Ajmv=<*oq z0ygwD$Q7Mp-vIvK0BSkcJmJ9AhBLb+y@GE5EIH!ywy69oXDmtEbe>%mV7z|b*hD*_ zvl@#umfnSXOh+?#{TptJI*7}7|?4PTE zdfbPmYt`trI4o6b;6xAjJsIj}MaIl@S*6;8ecm%@0N^C8Hn@|@-|$MvC0#vgJ((xI z81F9YL$92k?3@nBrw_^ml6Vs+c4rMI!tK|Ptf1BWco*1-NZN-K@~UgpG`&P$RFy-s zzNSew6jPgLg&N&UEbYX$(n_Gd7pmZ9-E$Pv%w8R#<*Fh6Xc$R0(bSAi~!4aog$yazH|C;=Q66GyjvY~$s zIvd1O4~4km2}U=F5o@I;Pz?fZ5AU;o#B);YJTT!wO%BwKf z-29{}b$7WTM(xv@8&$XZd;I8dVWo+sn-(ssJTCGVWyxhlyJI$wkLF+Gs>jj^_jBr4 zvH(P`UQHk)Y2_gdAA-0)oM4H~JT8YXVpO%nO9nySqr*uP39n`PY8X6i+qx%I*?gle zC2|xn-QC@%Uq$zmE7Ztws~PNu7>lL;bNE3yxGyBBGjmTT9|dQz)c2fieFIeGpK;nZ zM?GPY;bdod-mxwz&n``*xq<(&Bka*3)^!L!A4JV}14Y>{Q%QZj^mw}^^A{lqVKTu3V+=BMN)YQt@&Q~fncE9hS6H_-@rJKGN)1Hj#tX4tDRpektI$+p9ZA`~Id zf{AKcxJ9GyE`p_kQh}EtzOfh&3`&QHNHU?qnB9M32|X`$X|Aq*Ig#ql*b>0WaLVau zQ_WA>1-7CtpVJF|YD5$)z-jx`e3^aW-b-p2Iwz9v8g3R=JkCGJExa*_&uR?qD;<*0 z&g;fBkO*Lb{Y3c3q`y${8{mp+u=TzRrwd0{S1D{0zA>WDG5mD|_)+B5R>IWw;jn_+ z7CBIA3{fv*bSLW&Q!;rXIGr8O{pdP>e}oA;zwzpZIcjU)ODh<%eDcNO1834(iTk)G zE1gUngZN-#xrf6AUbama%4!UgROJOGlWcIkG<}OMK9ZFYecsNp+s<>nSE{FCOg*JN zsmoLmFooMy?6Dw95T)uHVM_RhVwr*!)`ZeQqS4XH@A;l7+&4hKk(_*RcN5XRsf&w) zp-;vH_VU0zo`5ImB%x{|Mum2$@k(S9lGFEu@AzP49QuWNMWs8L#WpT;yjDVP43!GE zV@3Cb$1SV2JsN)NNORH|DV7zNm=2mSLyWiiXs<$8*9zz^vVnm4N5Hc+&#~4!7*Vn34Fgo2G_B~^cH2+AK6uK|`pQ5ISVHF{VipP` zR7wRUp7s0V_Vc{&p*=9>&mG=j5Tj;(!(y#cC8SG=F2}%7`M|c$X$V~_2z0ziY))x#bWi74O#ubDt%}v+rLpz zcVBTWoZ;z!?c;jX|BZs68>fBm-x>IJ?sWMN=RVNakL+jwSQywluy9B)aCdHBV#2@z z&@kZ8Nm+ry%9tb~@Tg?uY)S@(EOz#h?4rJJ_HW-kLjPM8A(+b?NvV@ibFy!MXO=F$ z+nl*it5)!>*g5Gj%sUq-Q4pfI;*z6a_};u<4MMAWxa^fVXY1tu`h&E7K!#a2R;(9f zcQ3CYG_zPfH@&hNzx#vKWC+rfq0Kpw5Cl;=QFzPHfXYy&y(|^~vZ{Ev)!~gyP<2@` zLaWi6r%Vh8m6eU-!#Z~peeRn&KYu=FiotU95w$VG1C%T$V=svA^xg>HrX4bJHQfS_C&6_WW+!Mw1q@JR^9|Zj9eDZ4h zrLbHk<||>(-V=mSD_Z%}P-MAR9Kw=GQ%w?Z47otf<1uRp98jMt_T@WC4ny{IGF#7K z=VWD4{G^!WViNEX{#X$NF2(+9nQs3Rb=WRJyP8 zLcS{ET%_O9sjFW$H>VEv5B(3hw7nWR(Y^ymWMEBZA=P_Vp7wruO?ECp!Rt?l+!*Bc zjCH7455mfm#0`{P;^%{G>Nf>4rkIcrY7!Ggl54^O?mgEih*N06>(;5TSEWe+8Lhxp ze~JsFrbFx1_pTunsG zJyvyp!j^JDaMtxbN~gns)o8p<1)f7t5*0g>JRc<>&6F+_ zQh}!J$dKn$nYKtpO!b7B+L~v;75!pY%HE$QYnM}K?LL57)9PWPR_9)P`DGU5dFH2B zSZ8Q8r&w?kO6McNe^isJb)~Y_QzOsEnd6*6<(YJ7JfGKjnoU9kzSX5xw{tgqOX4L& z8D`;6^R3B>>b3MZ@N{r+o|)OY%WnQ*E#=)QJ|=m09W2ej!iSxwJ6*v}^-P%C-vEl1 z0ZK!8`^+X6X4PL&`NrLLDR9R32~p&-EJ8w(ETmXiHmiG;N<_{dRifp zPZu*3vgo0Z1;s3Qgqsg0z9$$obXZbhR16YUG8PdiSOM+eBH4W3+(H%X4OE?rqMm>C z;3GxRWiCW$j_cuZzZ9f;`AO#yHtGK0X*TM)WE{$?X<}8>t9w&GQo` zlr%-X7Q_qcc!y!lL0&V4_0+0(hu-HPwK%Zme|%BD)TV|vp=a;1Odkf)#45ZsNurDb zR}Z=2t|KV|8$9^79&3V(YGyg9=z-Y-WFZ!bb1$)9E2>bJ4wp)X&<&}ir3>^Vfz)={ ziRn=6l_;{I4}1v2cu5oU_^FIe2wsfrYhH6D(b5f6hL)Xb46+SXVR%q!#)A`6PU{3; z1%hU)x}HU#mTd3J710e;P*>h8DM%nA@PL?HbFLoj-PPJZ%hm?=9{3t$y){r-9Oe=x zEe6CC?Pg3v69Xb+);iHBh51y2mU?ODO}jSc*(8wVt9rz;<{uejA@yF>-q?tec&2M% z*TK5?^L&<7bjJA^^^$)7LT3X?XWbje9y$8}-9%n}n75V&sgXnc-hL)Q(b`LRcA}!^ zD0hAGGoU*}0u>t6VD7*pAl!w9HbfZcCV_!PgN8IV5ja#bAO?$)q5X4VQDujyOcHW- z10y@f7wzybF;$FxD>x!4p1g*PE-fGY_{Ii72=?l8pI#GwlqA8qZIrTA?}Cvh&v z^H^|6afmjp*V`(U%-J%WzE<%ZAN#TP^SzdM7t|wG!P+}5DF@nR-O+QN4|vW628MTJ zS(Cu=wUvT}`sn?JGKc+=jWvr}7Y~t%T-nh>LG}~nApeB{SIoW3v?DY}rh^a0VDzt< z@;RTDwd;GW5IC!fu8~!-H|_+*)_(&m)iH^;9%a|Kkk7ZxsqLwogNfezo!Q07DKDB= zaHupNK7O@&#)%WBFPiV@z)-8-I!IN=xJZA<6ONu=Q7wM&kw12=yZIUO{gHQRdfOOu zr*)Wt$x%iM{slJ35|-?o`U`uH*wI&?pU_EvlAeB3PS4*+8yEks{;hLb!VOk z9Kks76-CbHvyV)y`2elhfKuO3PIQ`c;n#a5CtkM7N3;Q3s#!F_>hG8r)tiWGS83g1 z2S0IW7pY)^nhK+02f*7*YzbM9@=Qv{^pGxkv}cD`a*e{pa|b4kG`;lEXcdg3 zn{;($Hx6X6b`AAn*3nAkZct8rVBdXMjYYhNNzJ)W7xlbz^1a%Czro-~`#sh7+fzEO zag}Pl?wKAmiCY&?LDQUX0EMZ+C8wB{Pa)bSc1v2BV?2*c+#c0?+1SL@f~M9rA6a@8 zh*xj(B&C+f@Ftznbu}W0j0xCiCN|0~Gto8NMSdwd7NMz1%@>=+VurBZa*}FYEUsy8 zcEl9FF6xhbF$BL8Th6_BJc+d-U7HZO%#7*f)J)!B4GETZ%^ZThBPK99SU96O!z?#> z+&CC26Q7V|^ay<=9(hR@HXiw+l|xOq&n}D9-mGyM`OVR`u~pQYs}N>1TkQ{8{+4`L zRcm&=4$9*1n4fnNSNfyVU8k+ud36?bZ-Mw_2l6CT>w8xi{T>gh&`)jz216ETuXaz;~@zWQ# z#B1#{nOB-?eh}l=1-oh%7!irn9~T-=e>k>f_I~C1TF1C?WtC{!x$bk07gY`GH-J+; z?w=%YG4uc1$*675ODYTQHo-=G))l}Q8c-t-!jlmzk^s6eQDV@{20>HjOdJ@r)jD+a z{;XuMBJw(9N3I)L1{}khIAqH1j()7qPR}gfH!wH|0pl2bEl{hbJAE~m0o@#@c_>9Q zWn=1)FP}Z8)@S;B#82rLRgIv@_>=6Y=g>V0Jp(n&Oy4FP^c-L?s{~Cq4wOLx%$6jV zt1qe-^3jamylYrF#|Ez(D*OcPl!}~{&XgT5wC5?bccbOAc7oA5-HMvn08~VQawaaE zzeEahBdx-m$t*DhtnyC^?t}h>ZB90voAjekB~)mk9>kU;+j_#>=ltbC8|m~K-_-G-;Zn3%btixM#(B*w^o4d*`ZE914#%b*8Crt(ZeFvj z`SEg)spt{r*BtP9g3-BB=7Noo`Fd?Uj#w4#Rgz(=Jx}$r(RES!*f#(IWAaIMLXP)( ztKJIsoXj>yS*513T!ns+(4_GzyQ1KRaPNW2X2~_RXV~OIG31P)WrJk=3Z5w8y{%>K z55YSF>0+mC!2^fKDfV%cyX3+5bPwnsarUK8UV?x|PeL>DKV$K#g_!OGYvhN~I)F6_ zN0<8jRbM-5_nigDmlS$%SnIOY?y>HCO{#`2j4^jkeJX07knmbg{>19tqvP=njkA(z zpZF*3dvhLLwNLeE;IScfS-c3VTWzwqlor*iIasu0Nbh%_sQTz6I}0KEny7Yu zp}wcRzDEw(D+|)3^JWr_?Z3`iWK!Uo4lD|1@RocSWJ?);g{GE=PCVZrPDIV)`7Ud5 zlWo?9wd|q$!h%3JGr9-$u9kSg0|kyHSF|&w+6Y7f{?t`J=!5-@)3zA1%Ro^#?dJM9 z8G%muRV>kB4@FBGh)=$(F|*}_$3SIKk=}FZu1n2ls@Y_Wd5pud>LB|v1hxKFP?{{- zmpvEGh0_on8F@mLm_2I<_UL9q{npl3x~_~j-vId;T{YvI-{QXa>=~#Afhas1qtyC-}><1a&pz)6;T)=8JzscMmxZ znN6H=ApNZ@i==Ex3fY_h3&UGNW$1i$Xaib>w>1cXR%2DzGxl(0uQ4y9TX#Do@_tfw z>wz;4KM3xOJ5V@*`nEBvlJ(=`@|Su3;Q6pg7@8sa#6 z$kQCnvohVBZ}N1J!O7umkfn7=kjh-9C4;@Yk-eyFOI8LY!62r^VO*y@4^9T#`?vP= z9Dhg{h;sAQWe{wAQhJ*^7fF= zCg;%g9NP?icwADZMY=)gYDzO!Yu!SrSv@zsK82+-W%rfUr8huN%rZlsl z~&ez@XeM#ABbV(%*82CwbfnnJIqmX#1%$g+0Ag?#!8IQC!6Gc)rM$B z3pr9>6vvO-&~HnfKX~9nrFM8NzTE0~WrJQu*7ow8d`ZYFdS1vf~lY3@XMbZ?u(tn~-aKKVnvIF>KrPFXoN zq%t}Y3xmoXz#NY=fx>o)Zuz|3h30R7wIlI*$CZ6X@$hN-!2VTysTwrv=&`c;>*+(D zGo|ta9me}#$U46P7^BXBAA1slNgC)K4z}F|axs?{2?*0Ny_2dAJZdwlV;_}x$|{@f znLvN8=$XN}8sfv^Y(2V_!ERj&tr*4;_0A>56HR6({k$u*{YS0+*7`MSOWf42(?m~vkF1|pqWCNSS8+IonWQcFIz7jMZ<$+@y_>vD09 z%lZLN9wm?6dRD_4JcgUn(!l2BgG3VyV%cs}hn0usEp;F02a#%CZV-fXeP}+@lFu9F z#*P=9D8q2)D-$nXKcF2;a3;Dhpe;H(j!u_2kRH(Fcr&d0ne?enqar9CD2dh%?h)w4 z*q6$V(L2nvNub&*A=8dIkE@{bP-PU!n7fwG*dx7oh4RBahH0c-`?175cE*z|2lWJu zw%no4zH?TUizqHn-&$OQIO|10DeWjpJUs1rul@WCW3J6|fI-SW=ENS0S5=$)MOdM& zT{wDJq3e&KaduWFqNfzjxMl{zZ^S;Ke3bN01U4pY6uRW#4@IP(+P!rGJreX~$5a8>^`s zyz5@L{-IGDE5HPLw2GJE3elq%y|)sK78@oetY1SUXN2Io=>VL7Pjw+TDPfkw^hg+EpA% zVWtX^uNzogCm+rsAFjLLXzAkDt86&iE7s(5A8qP|%e>+sMcm3={{|om-E|+>fGPzJ zP`y>k4(dh92MbvXqod8nj^ZYR%u)AKPAs9U@Gc8EKF=A;cs*M`9=A*|yu}$-J+)M( zQH~Ga9Wk%ztth^stj$Vg?qb(N12i|Uq`LPu+L!or!3kSNt`GFCdye7t@e~>n*P-}Y zmo*xvRtJ|=;?GspTyzQVCCN@NTB?@F$rQMNk(Dni&y?i~cZyUmtItY%e?(YrC_}IN zmH&_$rUR2#6iBOVv9Z{7)J0rVG{0?cQif^3TPX6R>}%3Cmwr`37_yR=JBfIOJgd-xz}zY2xLNE=~Q(L zGOL260-1_iv=yC`=D&0ReW52v2?v#bW}un8q`BA)kxeJha8@jw1#Mr3c!(BVn2A;# z=|tQdO}pdCL(`(W(B$nS0!xZE-Q)uXro9m*tNjyloEhI;q{Wq9Av1AU;Z~+$O-&Nn z3s!*@`NC-QExN_AYa=VY6xwS>U^~MeRrXy&DBK^!*^>m0Cm3DOY{#dTMyLg~PS+rmq*sKk0_|?RYPD5Nm^hdRC0J%?EFDduP z1h>iS);?WvOB%kNJjX8HR#;d$w0iQ&JZF(CHcO*BW=_b~Kb}W@!o*?#@^Xx;PIp`4 z_7rc**RSG_Dc0FPX!F{T4n6SNG^I+|F}?+P^;DN>c;kwHs_JfT=q_V+E9Ew{&T4 zP?XZNZC9!NbQa@`X~9FFxu%$a9=J>DpThZ37A&BfAD^=SnP857hc60x zz`al&3IR0_#~H-Mez`@Z5FS>AF}jT4HI0`mt`y-xO`Au|y?4o9&#qjHKjxNPH1Y|< z!T1US;pSOjL!j?eu{#L21y_XfgRpVv!@mMYr>^(v%r+XO!O`cePrmYbh6lUFy~Wl( z#L98((FxfU1v$(u6M2OMcVh06P3R|(XDM8-X2+wembqCfLk|cO#~# zc$DKl3iy4Q68Kcw$Yfj>meRfCRM}q0-5;OY+*>!!Ang$t(Re0%AW?CEDM-Di+aTS$ zq7I?ORJNxlN-`Bs+$$3Ul4s`}=ZI64cRHI@@sKuFJwLHycxziHJ0Sf?9mC&f>kOpL z78xX6VyT(DsQKoi{-n@GuYA$eyfUH~%1TS24xx!UiDKv~t_72#M~fR*5ygcR(ehl$ z9E+n#cNLRpM$-gys6!TQ6PC1dE@M*dI-RN7=2TvWW9D%3oT{#Wn05U^Szzfi-ssr1 zY3MbHnLw9zhoj5U)1RukL~yJBjA_VbXC4Bn@_KmTc7=#1l4CI{C?4cql69qi5r5@h zrB@w&St4|2Hh$eN{&AAU;7|>^QbcLoV>>>y;V25F62-K5TrsX zdcOg(LMD?&(+C-cLq9bSAJsE8zncH-aX@R(r&)Cd zJ2kZWC0_mM0Y3gC^~1;4OXH@^gJICS6@lins*yd!B+#VXr{Q23sU`Q*BdGx}5A^95 z2)MhYI&8+CGze;&0~=lYYpUu&KU3i2Ss4z`F7XgI9&r^X5{G3Jkn?a1mYi&_?B*!A zvMu4n1hq7thGk{X8OaQ7x^-8Vteev+Dlc3GE*8A9)rZ+{mIdiyGHQAT_O!uP#B|l@ zt&0{uC0-ajpyzcX1CeVF^H?=fx`Qa6s_Z7ooGwz5&iFqi=XHxe9!1NVSeXMIPH>P- zS2sDVMLo5sCUT37V5=Z+6clIz$>^@ztu+J>wpM52Gs-w=#aV|MuAU)OFS_r(rvNfe z6@PAS@Tpv=Wa}FwX9)d&p5A^Oge!jTFc#Vu))0JahDbMOZ1SqMgKG-)`CWkADs-MEVV2D_wW@?w6KO zeeG`m?Bwoe`@3s}cKpN-I$7x)4(!T*M-wwe#kK8wgNzz??xjz!XG6&sG}Oq(W!!64-Lq|5Ni{A!a}m3^e8m&MQ#&% z1MDoX%G#R`r=I*olMG<0DpBM&hb)07)Ll+lVl}0R;ww9ThIM!S@j_#Y=O3v2FZSL7 zs*Y`07hbr#YjAhB;O_1O4GuwqCAdRy*Wm6B0fH?E8r$L`g1*Q{AzRn6*Ys~0`{q;A9WippUcn;+ldwTU=Dv{M^{`xz#!90LJaOCKDf zMfLMVKgY!|>|0&~^<^>Zn(-VMmqNT}VmR!9P2@G6n8Hi2;yeiqwEYc$w=0`ffq9Ki*`O{9ED z8#x&>W_Q(&XjsZ?+>DZ@PbAv7wKoR?*z3Y+@Wm7y5g|lPuh_3x8+Sqn{8;71<=gPt zri>NBJvDoTCwUR8GuC9X9$}z(aElfqPNq4|V~F-^N9m&`v=H|BJA|2;L< zt{^WRF|TCpM!9q6%u)Bd^fpc{ z*aW*Ck2w+JKFLCf9KO0&wpJ>3u?>>`DziPE$b%C+yG2$(?uo7#>U^PW9w#*08+5#M zHuAGsl8}9NO-?Xv#@^DVnV4i-(z#{Hp{Mk(Wc#(BQU~Y9ZrHAGeQ|2_@If=gkVeaj z?0w;`j!QwyxmQ!;3@;zER)Q{rjpv@Wa!R(nrNBW^bS7RpFoVVw#iEju?VvD$o(!w7 z-kl7=_yWIjEXv4g#oZZcWQ%3#y|$^t;w8X?p;m(u-WG{%w&y7XEB=;@lB+*zzdP)B zdoTxeb=|1EwlaNZE5nk}<@=OLOC_Yl4SNFILdj~EmT7vbYUPV5yi_d=ivm)U6XjxB zqqK;Xh>e=bXWOo)cbt{xB06l8#gk^Iy8_{j@;Zq&@d+gxl8n8Be28S$#e=hMSBwbI z4BZfb@+yi@xVSDje9Ho3XPNR6Hl->p+aZ!b%FvnWYJ=ZQ(0uF@~pXQ zld9EOE~03u!Z1%*`XpgD9d0utw(QZ5IiNZix|{HoSNjx-XiVk2X&aO9l~)Am>}Zru z%_n;7TDXFfN#;d(PS5u3Oj*vt75dH9p(Adg~lRotl zHwu$|$3elk*Unvdz7jT=MILDCkKVy-?k#t>Z}IGbC; zo$+pq<=BIIYpE-!`6Z*bFo4*iD0VArX6Ytg9lzU+Co0k_(io$5SjjaPH!#X(T!BBB zY`;O0pV#q57=XoFx;P+2^#PHwn*<3jkHb7-JjH`p=GJa>fD-IRBola=eV6_IBkI*h zAloI#Tt0oufqb7SesE4nHM_Oqip&+c27XjJR==zRX&7(}A41p?2`28rdh*#ji@_){ zi#{hws?D@>wWt~6mn{5a-a>bNW2Z_3n3-#nig*Egam}-N%tN4Wy4^+3$OnWfTCS*5 z+X{VXSO^HEeP}2?3?+R=S)$|LWPn-u( z8$`MQq4*o8~yIHDP;ol^QUp&q%C6b}wm1YFk>lTyAicjkHm-%*ypr`=-` z<{2|6n#$o!rV;6?bb1=_BjY ziG5RjIr)rq?`d9lsb26nKAKU$e{>LXW*r>d;}A8$s8VpFoU|_hUBK=Zy(RgXu;dIQ zm0-Wqy=Vh_MK$P)e*cHIi_B}vnkqR)ZkfJFc^p@C0f(eUuxfE;9b!2xTG0(*Ia}>t z{+48C^UWH9f&Ue@l9|t#{M^*Vo69O7A7-2m7m9x=VgpLrc4f-PkCc?^!In9ay&~;T|jOCd=5N#Ky9>*okRitf^r{b;H<(rKJrJIOtbJAWu z6zr8&89z=AzMi2=jceLWj*P5y zSfhl84sJ?Sfn2mTWRO@FqpxI*X-I^09|Z~*)^+p5kH2%wRvioJ(?o(-eN!O)c#X@F zWnMZ?!%jd=aE2sXww7HhWeY0fjOebSN~4;a98b}0_xIcBjM*N!vCYa_&>x~gb(Op)%-_0vT4hGPYAk$ zem|W2z9;-q)TNswnWOu`AdOwpas>B{`SNa%8ul58{ys*eHna|W?`yqf!h>#5z5%`g z-pSvhhQ|wm{%ijmU{7Bl{=0!~%maU-E&hJDR0x@Kb8}5tx0oZ+6|uGu59Cv#EiM1w zB8X9V$+ExPs5c@EI{(;dW zy4RheAE4AX^L7=n8qHgTw6Kk)S!wzZ}1QUbSxk zstGwqQwbP2^2cRWBOGxnD~KwJR$6OaMMb{J2RrDzf8O>*$gho5wPAdi^UI@kQ^4*`y~kY#qJOlX~&O*7|r*r>$z8 zaM=T3R0~=CLkn^+)#7?~fZwkW=z+gt6XwSDXo_Qd@uH35) zq81;3-9Jnrc`Bz`T;>!@ckuoh5UWQCI(V>`&0{2Znp{4VW6`__Q~Y=ca*FrGxI!sC z#rijZ$ny{xC|qPDhC!_NDomZge3Yut*y=F9c_J#@c;W+5dQN|9p+wWKnpm>+w|&-! zmPf|5Z+hmLQOi+YX`7|gP*-MOK*pZ-mve`E4K=&^Y01wpJJs*L-v})uYpdvX3FL4u z9RuoUazR=6UMnq`_}7ZDGBKH*n#B?k9LO*Z9pbQg(9*qM?@esB?iyd1Ak6j5tsx*g zWNmD5&B{*SLaqY#U?Bs=+-LC zmvSH1!k{NiVl@!@l4L`gH#g$+TAkD1r&SrCo}o%g9k* z$=9zSCMphr<}_8$4Qsk-*o(NXdMUg`py!60hE}`9`z&K+N{Xf8JjNh(Vf+C5Oa1n& zQjU4Woc>;9Oar@<{X8ov$9Nj%!HAkaRkyl~_p?dD`>MZH9?u9@hx7zlwx=96d%4K; zQz8O?I2#Y&)|fRZs&cTY>~tNo$0w1~z;yg+gxA!R$E+8RMv8%Vr3_?Y_EVZ&0{A$* zYsnsHo9KE$nzGhv*!iEimII#X(_(Vd!Q^4*v=XqR$-G55)V@2EG^PBML(6h0`s9w5 z(@8$E%B3cLL!Fl)|HCTv=Zd;34&o6;n}{)V)kXYVM2YujcH~sH42#4ibfNmh=E{7+ zGIF+V-t(Qy9$u!MbGzJ!7q*cSug1iIIg~Kww*dMN3DGR4K`cx%!c#=5CCvt%)QU?# zI(%&k-W}F}F>7_WN{2$o1V`Ekm3BW;(IM#Z+5+c5u^Lu-Dw9)u+jsmi83RTOh+OF$ z2<~)nRA*t&OrFps^SYPjFv{0Y2M24p5ojuEJcicZUJPy}ydRE*9F+5Pb9!~x2_ci3 zn}`ldkn3M>LHxPS1x{pGq{(FP$qY((DGLF8kp(dY zoqF{wAk}!Yt{r+1h3jk6kC>AdYH$=eWIou8#T6m`GRV`D;dc-x%I=9kK*m$J{G^RG zB#|(nacKs>NOVd?H1+)?@Ol6%16~SE!`xcywKwfbok)wd#(9Q>l)rNRsw<>>jf6@nwk- z$uB(Fn%nPY#twO==X{e{V7;!PH%OGrm;YA!88GQMFoG+a6IYG}J&BPcUNt#4fnMB2 zcMr-Cqbu){gSTk_MjvjLds!0$oQlO<2_p(66kHRXZj73L-Eb(6lK zWr3a(89SqdD2j7CeXB3!p`K5T#tP_5)dQdrl-E?N<0umKlrTp@&)=YzM@(;V#4hk= z;>UpnL=12kkbR*|dMn)+r4cVmE(@H(BTp1#Co=dfgtfr|Y_z!YqY|#@VN4uSLl2q5 zx#dKUhc6^A#xH%0n>L8kY-UstU4YVN^589JteUrY$GJ6=x!?yIDS=@>MzlS7In<>O zVy9k68c?h=G`)#;+%#=H=RNQ2ed5Wu8S$`C#HZ{xg(J}z&{=2F2w0cTt(5DPq$*>m zfpU-`mLE7Rxymz2U)bt2q=_oyEUg>S)9Onrcxb0ZrsN6qKkc+8M3{pU&0_f%HyoHj zHyj$$UKl;xS*a>~I6;|}s20d3(y9L!C)b@jWQ*_nHM;OE=I9_-oQ~jQ&JiIAi>*Z& z24}Z-Z=jrXu2qJWOTZN`$n??N81TJ=M+deCYlr=0wNr{KG;SGElXzK&r)@=sFK^MZ z60TJGU)c8bJWn({*YsBVeusitis|!)&4@=8=ZCi0L~_?#sH}v+T#s4M%?oBJQ6PgT z(8wQ~)CBt$E-T5(VJxEs&eDi1wVm`}U4NuevS!3PbaaT8f4wk+UHU`+(C!Yas7Pf1 z^%FRzh{~3CyS6uP4GmSS0 z>7_cXsX@}t&jtzvO<$F#?8861;xkIPh|;6!cjr)wN**F=IxxQP930fdvj+I!WuHV3 z;Fi{7OH8{{TKGlC)6G){Jb|bM?>F~++N@~Vnlbb4fTExa(~MN&RwrxQUE^t8WbCa< zHYvwvy7zgdnc})pm#B&Ig2##UUAYbg2`Pw$xHG_guXMEorJMEChB=`IV~^@^e(5|g zQbh^TxUS4tBTfQrQprf_<`OXnPsI1S1-K}1)ec+nfg7yN#;8*zO!8p{GhuM%74(RG zF9`a=S_*nKJak|05v&-z;D_jEPB8j}lxt0mY{kPJ+P5*as_R}(oaTYWJo_c}J<7iJ z^Vd(~!W`+5Qs~yq8;vQ``mI&hFb?ed1Z_~gv5j-vBBvog@*GzvJ&(*-^h4EcT#PuD z-t#MVz#Nc?r*gNW-Oe-jfigS8;f%67GUF&-qRrogCd-jGCU1g|56sMOy(X)z#b4Y_ zdp1`=cDaB6`j-OhdAVDf5qQ#&GWMI?1u;!|GgGJAfp=bmID1X-gp-mvF;h9{7YI;; zi*uVb++3fZnmzfRDUTg}cO1 z873;aRb)U+Z{z`W8eA0l)lZR=Mk1p$ru1E}QxD<%UJC#4{6us~(3}r;%H*)?+_$@C7nA=azN}f&;tn%03Q*9wK+a zL9d=9>jfGK%;sC87Ogmk6P&8+i^Qh9qIuCMN$Dif2(&uZoMF$t5<5E=y79C1_rLb-lAELEXNaMK55bB%m% z)hz!gm8yUf5D}vRkA5O~B0JoQ8W~t=teqs*4@9g+B}<$Ox}j{QJf+WKagEFK#>+@T zpmGulsD2HJDx4t*-%@r5sN4KPn~eZkaO+lUI`vK^Wx5pIk~d*kheF(0H+Gr351K=z z*iuabJuo<4d2Cr`bS*z_fV?iWEUe(L7L(vDG#sO=2!UIt46I&w7QJx|>n@FwXd!44 zvpk;RNU`TgUCUqiF;IyzyM+vmI|>7%7mgxO7$hG-`9wgITU0YM@v{;pB~_THF0R98 zLRstrXKbrf_EZtdIMEkO-KC_IaA?cC4NCshabp2r>f}RpX7WA+gt8IiSNmf`)Iy35 zkP)t&;;6kP_uSeYdnFpE#cQ6D2=Y>f5)N)-O7#U?w20_SDnHus+V*gZ>C|t4fQP?3 z#pO1tQ-5z;PTuocH?C#6bU5QWa^*GB(jdeFgi&c6Z0?E!auRj|{1Kq{>(NukX?0z- zNlb>z%f$IhD)?UUuQfU~6fqSF*HYD|1V}aLGNQMSv$k(uzA!OGp|%u-M9kV+rWho3 zr0h2zoiPurq#pOq9zC__hN~h@sWKU1rQ_H+ri4dRVPb3)hpKP?22lRdlRxz55{lFq zVZaSXo{awZ88_U<<>L89u={V97pNujsizUXzR;_whYl^ytBOX^y;e z4FkhgM@$t(zNFezb-EhJcPPj(1J6ju*I}5k(QCVj$begy#oGGuMCqrKqjo+&Xu z*={@wrus5WG&-9S%kYupNT&Zt>IF4eTlQFv{pYO^ZK3i>gb>eN_H1L+(hF zOX&1CQqoRr@Swa>(Ysk*a5NS1;Ke=jEA01-7M&p;#1 zKR$1$Hz=8KQak>^d4_qS80L}n55^Zt+G~8W0Esk)ar0LkI=Lg*mUVxbsIA!G6t`sm z%lu!FJTxI%f|T0-4XyVmNtinl&;yf*5MZF?%kQ7cMMY%k`ETs+F#lPM$Zoh@7~)?5 z(aB)m7s!z>A-6@*eiy^^9zp8-R}e$x9_@D>*_usE1h60oEQ$ZWyI-t&{cr3mLbIQ4 z?hP&9o<)N!dhGZJPJ)t4wX2@D_xvaAXZN0X?i{v0h_zoVHp_~7far5)?xc25md#g7 zidm4%7?R{+8;IX5>-AL$#DDHFoO2KP45IykgNXO^nEKAq8UaVE{V%7FyRsWuz^!jq21={T%j_roy^M1kWU?*8D&ePa90_zAhm4*nGahB7(y{e=TyKg*(P310mm@)I(5 zHuNh5fFcH|D}2v^`T+sMd==_@&j4}0Z(o*92z)ki&<2T4d z;{Nc%bAay;&QHjXBsai;E=GWD3B!n_s-lq21UUF zfm!_ulI20*o&!lk0!EYj>LKnw%EAL7Od;wE%6ou-0n|DLa*zm}K{Pbb?m=}D2qU?{b~gj5>@?ID`4ab#=buU ze%NLAMLqr%0-|vq(m#uDB%-(WL*S>YBHu6m_p1*8U_hd({0adh5rHH59prodj|%fM zaew})^C$2BGRTnCV6dp&kVZYQ=%8u`ikKuUAOz7#Rsc)_0BXK}W(5*FkOCD_Ag_}! zX9yMq0Tbp?RU#CI2Z)Xmf^_^D1keSE+J#8+y@lOdK?H~bQ9EG)U=MBX0YZrC4|@y_ zU;!2AzAUIDJwJ=~zyUxokbohg{^UJCU;{cak3$LXc@JG5S?|wm&>3U}UVzM(QjIKX1Pm00rcCE$9zT%;BFk zfKtC#@&_w_K>Rht=#R2LI6tg_mHkNnw1Nf@{wvH62x95?0p~u*4;#4jUm<=v24_6 z2qvmOz#$O8A_~dcecxUHFpvN_L@+Q&R7i^7D|}F_48U}H#h~g>;Uvt10eq;*NF2rr zB)(k$hfq}1pT>VZgW3zIadm>~HX>+!E)tLxFaQY{02K_DMEFN-|5f~+RtA9Ne%U~d zf&Y^HGxGP|f~+6|*8WWLHy+5b-{^Y+WaURE_)qL#6MwYwYwn-Yzi(piem{zl{3+~j z=HC(cI|6@4;O_|h9f7|i@OK3Mj=RF!H2>#TPJ3Y0{)K!0FSF`?t0_vLfc#cX zC?-KhhkqvFeu>q;>CF98=V+jx??5Z-LclX`_KhqxS}qG`mPlTbt-YZ*_b)eEH5WR68x-z!nJj~K21hh8d_cajzBckA8t+Z=wKU>0@;JFc* z)R`_+hD{=3IFq$V6x^A*c1-L+Epadl?6;Y7;Qd%)mRR;Y37meq?+>bzAy-Ct&vI#u_6}g^e(~E2LoS89RQ)S zXUhTYJNBsWmo!AVIj8ON2p$uaLgc#)BhU8XJFJR0Yg33e-biSUK~8iC+~y)Wb|?rH z&)HJ;UEe{U^eNMaY6jbeSLjLRiU((&hX=eW#Wh-ocM*r6(n&pcy&N2bPlM+;Vky=_ z-%3yGUX@FfW5;_m7?wPv;RprclW!EFJ%BLd##&Zj)oTR4uZ^XbzK{3~V8&L~>D9H;tYm-%0M#9H||peqO!EYy^aiRBf)x`rH>x*9+{s z5+C)BQp0KHkIA3qt_yBe?-{L=cQA*Ii*|q5p`+r^@wZ1QMxhbuDiw1Np&8WP`05J8 z(2H~Ad|`5%e7lX5iv<;dWtx=O#`onN70`9J8>UFo(`P>N-C~@WlNP}~ToHd=>!4S& zx|@VF&@v(}OOuzI#nm67DbPN3>n;sC+1>ZlzG49u2M5296mN*(b~C<}VFD3z!YwqA z?267m*J4Dlx;NJqqdJ<}VLAe=7Q5A?peE^@c~Bkf`8R-(Nv{+D{MLB@42uweU{IBK z!(}McXln=EF_3ZFS}K$+;VkGy>p(GHNl7Pwv_^P0_GHIS>AdY*6h?N?L^=J zR*x*3lUaUfqFRPjR`nHk3}?F@Li(M(jtj0#j?EBwB@>h(8rdXa3HHEtHUQH!XYe=@ zqdqggpljPdFIN)*()whOaAw{})58he!Kozc@{7NvcmuTwnawBUsy^$%a!NnDPw>su z>%?c|0XGF+y)XSQMN&GUF^#_tskV9htBD@eqxu^R2DlxT5dv#XZZn=?P%5e@k}cJtF**AM zU_hV(mBjGWE)E#CV0lrk}K|zv6J$3`x^yuC$YdXyI zyy_QzzKCKUJlNq+Kk|m@*T59(sgXlFoZ+JR^SJvJd^{E<%we#{lPa!aWzeKtB?O|U zia!=M4m(b^eyx_MLR(qB?#hS&=4Wt)85%J^;ZySbXm^k3*XhlFN-x}n38@ne4M?rEJrpo0 zP?x2nNejn4=uHs4R@6E0RYOusNHc#u*~e>+{|eR?Qp?;O4I!)$f7D_GcRk&FWwH;B z6wNHk6p_A;q&kh1?H~>}(we-Ss6OGtB@QA4H;n7s39wtge1!qkI9?cJ-##hAHv>E?45%x2#Ss@Yomn*F%7N`8qF)k;->J>X;>fliecGl2Ld zby%Za$Pi}WOJU4&Ku~6!jsbwy!YoO>X}D>aE3(IDt~*3M!@>Fc=gJ6c_-s0L*XWu!?c)v9hCIP1pDdDvNN=I*F0PudBbHegm|Y%eX?~ zzqjT$=u|4E7a3G@QMq<@**vm-FOvfL(l zGO(rHk0WlRXg4xAQ3k;M)?5DhWKvoGH$d=O(Ca7`l8J_ejiVRqNf({O4y>_8h3zkZ zc_vFKO{=53u%q@1>ql(3yE#u7Z`Lxz<;pMB**8p}WSQaH-$t0iXU@BA(y^*HPPCkc zX)}^{MCbA}e$TboJnL_cSMN_(I3616b?HXIM{r%uH375AOkeBEg{37R zlM29VcoRw&96NtrIx?s=(XYp+;f=5#3|z+J+tN6?rgRl0=00S8vqHVVRLF@7X`fN$ z`pmpFH-bZBNJ50WI=In_v&3jMhsYF6Ec`7{&BQ)K{K6=zQ%dO4D9@A*0?;~R%96>d z8ERbOn_&C=#pdU#Meg1epQj8GOD(1evv47n4qkXhPd|C5u|g`>;imTB7gl!$O74ED zf1kWrxC1tO&z&hB}A3`7P3I^axG2HCr$q3Bp<&G4hy zsp}|h4w=}@rYfAuZf#s$qLkpmp@5fMf)^OO`PtzYup>Ict$7!B9v{2}Q?)~hQmc4O z0_&kn1DR-Smt3dv<}i?Amo_pG<7q#_c)(cMyvHrsi)?!<##p_2{;@SnF)!e_&Qryq z-R%}*8tX$YSJ9tUZ&>xPS_=O zD-U^=hF8NWaMDllckK>9;-0P5?c-vASYhP1t> z8Gj%C$6~;Ir=CqVVyWJ+=<;+ei_cer(Wvs1^N#WhtYw}irPq>0e%866TQ=OKLFZ|4 zjSB`s?rhz?FXr#SvEzo{!=&;Mn~7V~?RB)6$W^loYG7gZtJZA_YA}Lxsym+J#Ed(y zqXsL@zlCS0K+l7s=snuP@fx_sSu2AW2)Po&#qZTgCLi=Uz=tMy&e=Uw#2pU0|uy zkfwJvyjwIu3-I)dRJnMLq@oJ5=z;8(!qQXZPWauXH@qejD`dCc3!FhlT%y>DvlgFV zbc(4hV9jzfFTKqk5jimBQpj+oA43E zGReP|wy9??9>CT(WTKiBa~eQ7A_t%>YrhbZ*@59K35asZ)H&j-TR~a`-+p6t5yMu3 zx?;{qs)o3MMal8Pxlf=GJ>tmjB;_jV* z@SP7;t}yj+23PEJ%!syOA$pAR-mG`F#2^3JY zjfRP6Z#HPdOD<^UTc}vR$^C$aTG&39iZrnG;{7G_!p7U1Q?KeV3t_UfS9JD9!RcqK zOqQB@tgDE)hyh>E$Jx5xc2_IT%;`pHXi%%(Sy(>#T zRTkbj!Gj$aoi-sfTZWNm!6*)1xXVM*6(?x(0{YG^nF-RCk4E1~#U|*bkA5V8zEo>TZm<&(B|lTtE5}p)=14TsO0j#Kvz&n8)jE=Wlj-%Tugc zMBY)3hG?gYbQ=6r!Fd`8dPz@V$qozGYqqKd0%B$={jY5`UnY9+J|$B;QDEikyP9>N z{`3MBD?0%OcuO2tM99F&93p41#Pyn)P5+4cu(b%kOancXvo-t9eNG_8I=HgG-tuC= zoW5C~SR0-MchAjOdlkwbS9|G6F(n7(dDPA$yX)L)VrJY6(Z0awn;7#Uu4;J%ceJh~py-$~x6+D0*3VeoUF2~# z9F|tgvDR%jd8*IPEU%8IFRZuZtx$n41LcwG+G$-mD_5J-Rg_>A`mH3FYg6CSSRJxu zTJzVR`%D1UFd7@h#;w}Annp8#!q{za2ZLJddKk8ZVEK>~2t1aTeacaTjJ!Ps;AQ?_ zrU06DJntJeuH^#JLsz_+@D?$8zeYpL-BN3>VyFh-nuqdg4t7)z8fPEMi0-*8k%4Qo z`l!fkNMb1Ag%c2z-;Rj0DoEO4iSrx9@@XsRH5b+Kn74c4>Sb65?(JT$>`-4YY zO`uptO?HKfwdQAqF+-WL%Ln54K$@)>3B08_$v5Y)a&-(^c*rs z_-9`OIT3?3@kJbKuRN_`(aJNg$SX0yGe^hrs9hqd729_d+dpJ>5TGnclQ6d+)IT@i znX2yhD`TyqLBYczNG6MQ#)xsW=k#;-e_dC&&TzaugA@p#S)g+IHCpoubHnPL)oy2V|>cw=}?YuRTh7;`V= z+G-veHsjG?5@jieiiz)?YfOLLNP7%=G+%Py-7Pb$Tb4W=KL#otj>l`K-QaD*b`#pU zxTz42=v&4H)3&aDL z|5`|-AOw4Qhi`UZ)q@~7B^IPl)0cCZo=C>QkS! zEzsJ|LoqL8o!GGZc~-;OHS;H8sjJ&6t$X!YjsfD67v*hs*6mEFjyYd!P%(8K6dx6N zu3x*o|HrB7t*K^eU!zM=*=A%szFt+n^OpIdpQF)Ju16ULMW07-u{p zbMOr7I;Do`2a%+V${k@%**7@SbYD0tFIDzlpL9h=2CVQ)cCut)zZ?pN$HI;p%YNBs z!IMUOR=j>Gm3*8@YDy>ncB14ZzM+-+LISm8w`bm=pvsplny*R;UfHi0H0`tQeSCFo zNRtA)PE@x(aJ+Tt4m6t(^gC0^j!{lPg2bw?}u{&JvWDu(6@ zGfc!7?Qx~&a>18HCr}S!Y{Q)fUWvjcc~h-`YM5Zkgw1nv@A%QZlI5(Dtm@x%6Gzgr zJv$0BLSRg}1$yQe4zFcMv9CB1j(Px{AMcIw=4DHr4IHrS7Xxrd@eHQxR<$V%YQdsCds6=rYnlDdW6g|6q56kc$5s%K$lApG#ah+ z%SVg!N(i+p^N7TxXDaukCgL6gdlX5>v!&$_N@xji=M0D+KJk0 zPYcPt$!mnJR-k}|ib^TF6+P2O9bb!$7MqP4?a!QEtm1%A^PpY!#7RYkR)TUx_#TfA~<>K3w0 zD2A|WijX6t2!!7Ll2CjL%XBc?M~9$}B?tyjqeF6moXj{CxI$x2wA5bFu!E0a{Cv46 z#KN0b_7Nfm)Yj8H&kEss>-Ns4Mk)a^+!1^f5*iExmUhi;+Hh@hN zKl6kTi-5|R3Z+aobU3|T7Hqq<$C+``QE6|Ee;w#5FZ=hN=xZPzZ?YhydErVnyBs&VM81<$9k zhfcY63}KBF0FS3AgQkm_Zh5;n#o3gZQ6=Q7Ifir$fJh<*1%ep)Mj-R&VxEEpHKDaP_nHYL6(E67HlGKy`2uPP_bQojhlwW%k7Kjp57ncpfaWh z19KujrZkml`QcVh6GaZdV!f(ZRhV^vEE_VdIig&tAPDc!z-F@>r&`&BdCtn!XXVA* z`lzKA4ZZ{)LV-|vH{`&B)g(j)j8KmXuHno$EdZ&AWFbcG>6kKd@2jKP40TL4Y<_ZC zaTFrZobXeEGHn`CIDV6k390BYKZd(0l54}F`D#@==n8~VQW!BP;#3h#K7J%whka#K z|H(pb<+|A2&TqaH#(AsL&M3a%nF0{VW$`ZPHS3Wu(Oi63)#SO!vsyz;td*CJzG1?; zt)#5&A1l*R;`8?6SA&8D(D9t%8VQ?B^p!9!l$e(%_eWqa%{oS8`;^6QcIOXa={&K9A)m*^hv&$um2~6TFr2d zZ1llHLO_xOkPpIWf!*LlS-DeLatc6blQ;8xc}#mcHVWZQ+eWURJEq8cKx6dE;+WUC zf8GnZwN%RHC!e(vicKrnDn4!}7+TAUD1ct)>myz&OdCj#>pyNiY0*ViYl-`KJs-9c zLSpD8xNVA>V}(5?F5@+$CTLauaA4_)YrWm1}A6ZLSn5+4oIMr+ezq2L_j&`x;hiYcY0+;?i5{h@gKT z&vT@M)iT7`-QbU8eo@mSHD<$xkBqj!pADE(H>b6--LS&+r8Vc)+rv!^CFp}v#{tuD z9TPUl>0-0B9q(w*+q?k4;qe=c$MK@%ZmYA{SueO%mcc3%7rn^`d#UUHX);4iMGO+X zhA2pR%1f2x8$H2RKc4_QhM2X+GFXBa9d+{yAxk9 zVPyirFDn6c-zRHG!%o|SDU>$ClK z81onW>{9RQzMK=Kc)a{;>QP`ZR(*s1)v4!|pl6n#QWo`S{LX+*_s7%wiJt}sb_m?r z)~~rSoP0K=_cJ$;>PLYWUfBKi8PseJ83WzU)NsQ;NwVv{LW zpZs5DtWhKr8dx32^`1Vk%I&rjXFMyJlaYJN|2R!dAz-&NcN1Xe?#m@G<+OJ6f}F+3 zRi8*!1IM$^3SJ9G54 z#VGqu>e+BZl2!VH_i*a@@Ggryg5qme7y(;~D6t@tEg`n6;2k$*usXg@Hp;zXbg<0A zmJDX<1qXpFt1c(6BB+5c893jQ-KSoKW^!el_Y?AW!t1C2X@)|0;OB45^15KWY#CX@*9BK;7+miCe!VT z@95NJbxY4+d13k9UYYsO8&>)Kj)6al5zdk|Soz$~{uOBPX^$}OOMUg>J`53DZAq;g zcH}wXFA>(6H-4MDu8+S_`T1$xlRN?y+|_vd{`)AXp8aderzjuL1ciRozmJ$xpG{cHdyCc1scMSR5?a#CnPpk=sMmTN%k>cET#>q(&;tIlTEUf@@&T9x6r zD|Zw=-3?&O3mKCRb?sy{nr%=>RT(*Ebl1^`!o$LUiq2OLZjuS8IH5}rRu0+`k}GR~;;60jDHde{P3uTT zZp{%^Y12ap_bF^WdB51{VKp+a?vJPHh{!ckeVzRbmonQRML43PKBv;KuKYMNXK%CW zqp`4Z#To%mPhpvto2w*)GITf8*6>A;HJyUuVpJpQM&ZoKCQ~Nqve|J`S6>YI>LKB( z2AGl)pVV6QGzb3j%3uJ1ri{DBB}tKSMFhwZ;*zu9;%=d}lIiTENq|O_TPPLycInk< z*4RF!MMeg8htKZmH$Ztq3BHNhqEtLs6?ie$Ai76BpdNZ?5`A;Ta%jTNT~-uOTHJUR z8b&qIS+&@WNVYMo*22Yt#jm3C3ET`39>IuDsuK!6ReiTPlROYFshG|aTKiHD8X+K` zro3cTGA6c@y_F3{Uj4amf^t)vE2}!B2;JC|_8Urq@>C^UXiXO6E|{*1Bw8emc^x{7 z@B~b~YT%yUhe%oqN6%dM0w$FnaP0KuYB1ijd7tAVsc=eEF^r z@d@2rS{D%n->#LjqA{FDQfQ7M9{2*$ZfOgSnNr>iBt-ND(HO)mMgSUC$z8B|aV}Di zZgwLJD@M1kT}YN(F?4UBTAlf%;3U9Pv*&WkT$0EznIUSpgQ=h9WWI?K&z3@o{Y-wa zdazv5+k1hk`_Yr4zt@5!Lr<~k6`$s%#9`QyCASY{i{Bo=AG7lGba~B#*5a!m-YTxm zG(^?!Arn+EQ+aSEm~wYE=siqC?u4fSX0UrJ(u1|7ZzmtR)x6BYq`r7*{x#$?A`f6e zPE468I7!p4Z!~+@C2iS6-khJW9A(OkU9Kzvtntws7$;e03;m@uA`b-;o5eO`2)b#1D zGc)I${=S|bBl84B_K$!hvQR~|6?EHCX8oXu$&J>#{1Bo|)i~;Q-5yQk2BjbqI_GFb zYo-BL6=Xlf)CggS>$5N(IfT#2OUd5w^FUQ*3j0i7)sCT}2xr!llCS`Q{HTuHgB>Fw za49z(WZdk;vpt_DBmwTAIqrNEp_4@o7*SP;cDmJg_xWS6LXs~F7Z)*}O|=gE^j4-8 z=Tre{Wo03R%E#$Pn9)R)nnHe9thQ0h96LlU&iG~iSJTjDc6uYk^ukEAR^|x3cZ_R~ z*&a)IiFv5sl?r7_JBSdXP~kg7lpkA8{KAlL!<{B=UVkxZw5_wOSE{HovGyKJ;wGLd zNFj9`M*a9bZjHcvTK+5s5lHIfcm@rNr^li#8CUuSmCmx=+WV<;8fn@MNX%?AZ@PqQ zh`wijB#WU(~x_+jjWREjCz$a$WNH}yBnk)2sJbdv}z zE6Un3rCj_(uA;76@I^WRH4vwo4v)*0X}wvbv}9Hxpi%)hoB>B9Ewif*H~c062{c+3 z#cT_B)~K8F@UT|j?1CswX)@8rfi4SidwN#67rmKR>$J#-n(8z>@tiS|jW^|RM5fwYrl60Z+U6mU4+<$^Z zyaKBiQ1hbm=NNx1F5BWwZ&i-CilsNPIjWO@QpVCLZqT&nQB2bIDuzR4%j4z<~?I zBWM^=-p@T<-dh#Q3(?S>BQS;@)cs&1US)Rj$S4#qmhuvd(i zAba1jT2;~IWQ}w~aV_jxj>0yk5#?F-ZT&knEV5HeyKV z01UADO!+$xyZ7^##XYQp&o&dHM=``lWf8H-PY#NGl<4@)1S9G^Pv@!V3v4#gmE&XJ zyxQhV#`PXG)H{cS+QU2f%vEdMA}MDCNIHD=o@xgEhJZoo=W0UqC`p8i8#Bds;;G@) zcHY~!;to7eP7<;?FONBiF&!8FG9~B2(tm53yoJO;JH(X;l^9@V-11J(d;X^Xkrq|WDNi9obF*MlPkItrgJgM zssT9yXjzd%XiBJ^Z^@F6Oc^swf*!}o!cYRMiDcvKcow^8)aou?xKvTIQ%<&6f zPkl*vqeN`pd)&CM)N~Vd8q}@jF^zRspCj(E4q5Q(Y@;qMw=9x1xjzLw@FH?dbC`MO zNheV->!)l}Jzu(jHrgEYJkyIw;s?Xv8MhdCh_`%DH+SQPp>f2d*87{Vu83J@)1Mt2mZ7@FWuxKCfcDlevIyxgDW1TX3w)C*}lAZ%YKT3{qg3LOtV>C z^WxXkl?Z;*6tCvhZAO8hc>l!9d(9u_ynf(MQFQIL$$2_?Z({O9_7aca(qTDj?q_

u8D%Y~r1`*rEj^FiG#dUpxEmDD&+O%cV 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 H`TXAijF&<# literal 0 HcmV?d00001 diff --git a/readme/1.0.2/3.jpg b/readme/1.0.2/3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4db129ed7e4d5a20c579bb10a63fa7d636e39d25 GIT binary patch literal 54650 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 K-X8qN*gpXBuP5UG literal 0 HcmV?d00001 diff --git a/readme/1.0.2/4.jpg b/readme/1.0.2/4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d1b4dc4fde874b2d30e03273eefdff615973663 GIT binary patch literal 60151 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 z zm(N4Du--bkZutS{zlL_m5?qAZnI4|DXarv8y}#C=K_t@x$}2fKyd{y+mmi+=yX{`Y zFmFgKF-a9mRc|_H*bXD~Cd?-MkvyzU44pC7Qg@pHVcSuxBG@gm@apL3LEZ9KYb@b{ z?6vNaFQrp1^THQ-c~?-=i^;5G?PC^!WYCOdGely_|m!&W?Y$J`ue4oqDM4t*k==Z8F{93HZ<9(m?Agh z&)tFRVEbR20Nhvdkb+U?+ri9K+4VncId&t09gYDXcy6VF?v1Q|d}3Ea(+>To zMre`~c4cmf@;!LK82w$Y$d#nSvG?5aS@pLqQ#WMCzgD~%nRhHf>L3qV_UHKDt`CX@ z@nVx#1Ebc%^7{VQ`t#rQ-ir6Q97ViNTI4u+Hfra+Cya2*G!Y-+XWUJTb5k7L?5H!mzYG5cW!>=5 literal 0 HcmV?d00001 diff --git a/readme/1.0.2/6.jpg b/readme/1.0.2/6.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0070e0f033d0b4f0e35511ef0472a340330476d3 GIT binary patch literal 100947 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 aku39nt55%0+kXyEhQ0qUz`(V?mj4G+ylff( literal 0 HcmV?d00001 From c400197f97979f93f6a803cfeeefa1fb5a9e0a4a Mon Sep 17 00:00:00 2001 From: KenZR Date: Sat, 23 Mar 2019 15:41:25 +0800 Subject: [PATCH 036/100] =?UTF-8?q?doc:=E6=9B=B4=E6=96=B0=E9=A2=84?= =?UTF-8?q?=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index d48e23e..f28ff36 100644 --- a/readme.md +++ b/readme.md @@ -14,12 +14,8 @@ [安卓包下载](https://github.com/efoxTeam/flutter-ui/releases/download/v1.0.2/app-release.apk) ## Demo 预览 - - - - - - + + ## 项目相关 + [apk包历史版本](https://github.com/efoxTeam/flutter-ui/releases) From f2af7d20f03ee65a6275dcb7baa3b9bf6797d427 Mon Sep 17 00:00:00 2001 From: KenZR Date: Sat, 23 Mar 2019 18:26:29 +0800 Subject: [PATCH 037/100] Add files via upload --- readme/1.0.2.png | Bin 0 -> 42958 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 readme/1.0.2.png diff --git a/readme/1.0.2.png b/readme/1.0.2.png new file mode 100644 index 0000000000000000000000000000000000000000..f539c3b0b262704ddb136f7a5e13a3924563d2d2 GIT binary patch literal 42958 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 literal 0 HcmV?d00001 From 97ae1c21a3865e889fb8debdb45913d48b93542b Mon Sep 17 00:00:00 2001 From: KenZR Date: Sat, 23 Mar 2019 18:30:07 +0800 Subject: [PATCH 038/100] Update readme.md --- readme.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index f28ff36..171da2a 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,8 @@ # Flutter UI v1.0.2 +> flutter 开发者社区 + -> flutter 开发者社区 ## 功能清单 + widget 组件教程 + 多语言切换 @@ -13,9 +14,6 @@ ![安卓包下载](readme/apk.png) [安卓包下载](https://github.com/efoxTeam/flutter-ui/releases/download/v1.0.2/app-release.apk) -## Demo 预览 - - ## 项目相关 + [apk包历史版本](https://github.com/efoxTeam/flutter-ui/releases) From 4623dd8569f35158f54c9beb86b975a6df57e5ae Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Mon, 25 Mar 2019 16:49:48 +0800 Subject: [PATCH 039/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E4=BD=9C?= =?UTF-8?q?=E8=80=85=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=A2=9E=E5=8A=A0drawer?= =?UTF-8?q?=E7=BB=84=E4=BB=B6,=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/components/widgetComp.dart | 79 +++--- lib/main.dart | 1 - lib/page/component/index.dart | 167 ------------- lib/page/home.dart | 50 +++- lib/page/mine/index.dart | 227 ++++++++++-------- lib/page/mine/index_1.dart | 218 ----------------- lib/page/mine/index_2.dart | 156 ------------ lib/store/models/author_state_model.dart | 38 +++ lib/store/models/main_state_model.dart | 3 + lib/store/objects/author_info.dart | 8 + lib/widget/author_list.dart | 6 + .../gesturedetector/demo_force_press.dart | 60 ++--- .../gestures/gesturedetector/demo_scale.dart | 10 +- locale/en.json | 10 +- locale/zh.json | 10 +- readme/widget_progress.md | 2 +- 16 files changed, 312 insertions(+), 733 deletions(-) delete mode 100644 lib/page/component/index.dart delete mode 100644 lib/page/mine/index_1.dart delete mode 100644 lib/page/mine/index_2.dart create mode 100644 lib/store/models/author_state_model.dart create mode 100644 lib/store/objects/author_info.dart create mode 100644 lib/widget/author_list.dart diff --git a/lib/components/widgetComp.dart b/lib/components/widgetComp.dart index e690299..dcb2f66 100644 --- a/lib/components/widgetComp.dart +++ b/lib/components/widgetComp.dart @@ -10,6 +10,8 @@ import 'package:efox_flutter/utils/loadAsset.dart' as LoadAssetUtils; import 'package:efox_flutter/router/index.dart' show FluroRouter; import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'package:efox_flutter/utils/share.dart' as AppShare; +import 'package:efox_flutter/widget/author_list.dart' as AuthorList; +import 'package:efox_flutter/store/objects/author_info.dart' show AuthorInfo; class Index extends StatefulWidget { final List demoChild; @@ -25,48 +27,67 @@ class Index extends StatefulWidget { }) : super(key: key); @override - State createState() => IndexState( - title: title, - demoChild: demoChild, - originCodeUrl: originCodeUrl, - mdUrl: mdUrl); + State createState() => IndexState(); } class IndexState extends State { List _bodyList = []; - final dynamic modelChild; - final List mdList; - final List demoChild; - final String originCodeUrl; - final String mdUrl; - final String title; bool loading = true; dynamic model; - IndexState({ - Key key, - this.title, - this.modelChild, - this.mdList, - this.demoChild, - this.originCodeUrl, - this.mdUrl, - }); - @override void initState() { super.initState(); this.init(); } + authorTile(nameKey) { + AuthorInfo info = this.model.author.state[nameKey]; + return Container( + child: ListTile( + onTap: () { + FluroRouter.router.navigateTo(context, + '/webview?title=${'GitHub-' + info.name}&url=${Uri.encodeComponent(info.url)}'); + }, + leading: CircleAvatar( + backgroundImage: NetworkImage( + info.avatarUrl, + ), + radius: 35, + ), + title: Text( + info.name, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), + subtitle: Text( + info.url, + style: TextStyle(color: Colors.grey), + ), + trailing: Icon( + Icons.keyboard_arrow_right, + color: Color(AppTheme.mainColor), + ), + ), + ); + } + void init() async { this._bodyList.length = 0; - String mdText = await this.getMdFile(this.mdUrl); + String mdText = await this.getMdFile(widget.mdUrl); + String nameKey = AuthorList.list[widget.title]; + print('name $nameKey'); + if (nameKey != null) { + this._bodyList.add(authorTile(nameKey)); + this._bodyList.add(Divider()); + } if (mdText.length > 30 || !this.model.config.state.isPro) { this._bodyList.add(await MarkDownComp.Index(mdText)); // demo - if (this.demoChild != null && this.demoChild.length > 0) { - this.demoChild.forEach((Widget item) { + if (widget.demoChild != null && widget.demoChild.length > 0) { + widget.demoChild.forEach((Widget item) { this._bodyList.add(ExampleComp.Index(child: item)); }); } @@ -103,18 +124,18 @@ class IndexState extends State { } openPage(context) async { - String url = this.mdUrl; + String url = widget.mdUrl; // 加载页面 if (this.model.config.state.isPro) { FluroRouter.router.navigateTo(context, - '/webview?title=${this.title}&url=${Uri.encodeComponent(this.model.config.state.env.githubAssetOrigin + url.replaceAll(RegExp('/index.md'), '').replaceAll('docs', 'lib'))}'); + '/webview?title=${widget.title}&url=${Uri.encodeComponent(this.model.config.state.env.githubAssetOrigin + url.replaceAll(RegExp('/index.md'), '').replaceAll('docs', 'lib'))}'); } else { // 加载本地 String mdStr = await FileUtils.readLocaleFile(url); Navigator.of(context).push( MaterialPageRoute(builder: (BuildContext context) { return BaseComp.Index( - title: this.title, + title: widget.title, child: (context, child, model) { return MarkDownComp.Index(mdStr); }, @@ -140,7 +161,7 @@ class IndexState extends State { onPressed: () async { FluroRouter.router.navigateTo( context, - '/webview?title=${this.title}&url=${Uri.encodeComponent(this.originCodeUrl)}', + '/webview?title=${widget.title}&url=${Uri.encodeComponent(widget.originCodeUrl)}', ); }, ), @@ -158,7 +179,7 @@ class IndexState extends State { color: Color(AppTheme.blackColor), onPressed: () { final String msg = - this.model.config.state.env.githubAssetOrigin + this.mdUrl; + this.model.config.state.env.githubAssetOrigin + widget.mdUrl; AppShare.shareText(msg); }, ), diff --git a/lib/main.dart b/lib/main.dart index 9eeeaa7..80bc417 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,7 +22,6 @@ class MainApp extends StatefulWidget { class MainAppState extends State { // 定义全局 语言代理 AppLocalizationsDelegate _delegate; - @override void initState() { //实例化多语言 diff --git a/lib/page/component/index.dart b/lib/page/component/index.dart deleted file mode 100644 index cb42878..0000000 --- a/lib/page/component/index.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:efox_flutter/router/index.dart' show FluroRouter; -import 'package:efox_flutter/store/models/main_state_model.dart' - show MainStateModel; -import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -import 'package:efox_flutter/components/headerComp.dart' as Header; -import 'package:efox_flutter/widget/index.dart' as WidgetRoot; -import 'package:efox_flutter/config/theme.dart' show AppTheme; - -class Index extends StatefulWidget { - final MainStateModel model; - - Index({Key key, this.model}) : super(key: key); - - _IndexState createState() => _IndexState(); -} - -class _IndexState extends State { - List _mapList = []; - int _isExpandedIndex = -1; - - @override - initState() { - super.initState(); - this._mapList = WidgetRoot.getAllWidgets(); - } - - List appBarActions() { - return [ - PopupMenuButton( - icon: Icon( - Icons.more_vert, - ), - onSelected: (local) { - AppLocalizations.changeLanguage(Locale(local)); - print('local=$local'); - }, - itemBuilder: (context) => [ - PopupMenuItem( - child: Row( - children: [ - Text('中文'), - ], - ), - value: 'zh', - ), - PopupMenuItem( - child: Row( - children: [ - Text('english'), - ], - ), - value: 'en', - ), - ], - ), - ]; - } - - renderPanel(widgetsItem, index) { - String nameSpaces = widgetsItem.nameSpaces; - List _tmpWidgetList = widgetsItem.widgetList; - return ExpansionPanel( - headerBuilder: (context, flag) { - return Container( - // padding: EdgeInsets.all(10), - child: ListTile( - onTap: () { - this.tabClick(index); - }, - leading: Icon( - IconData( - widgetsItem.code, - fontFamily: 'MaterialIcons', - matchTextDirection: true, - ), - ), - title: Text('${widgetsItem.typeName}'), - ), - ); - }, - body: Container( - child: GridView.count( - childAspectRatio: 1.3, - padding: EdgeInsets.fromLTRB(4, 0, 4, 0), - shrinkWrap: true, - physics: ScrollPhysics(), - crossAxisCount: 3, - children: List.generate( - _tmpWidgetList.length, - (index) { - return FlatButton( - color: Color(AppTheme.secondColor), - splashColor: Color(AppTheme.mainColor), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Icon( - IconData( - _tmpWidgetList[index].code, - fontFamily: 'MaterialIcons', - matchTextDirection: true, - ), - color: Color(AppTheme.mainColor), - size: 32, - ), - Text( - '${_tmpWidgetList[index].title}', - //overflow: TextOverflow.ellipsis, - style: - TextStyle(fontSize: 14, fontWeight: FontWeight.w300), - ) - ], - ), - onPressed: () { - FluroRouter.router.navigateTo( - context, - nameSpaces + _tmpWidgetList[index].title, - ); - }, - ); - }, - ), - ), - ), - isExpanded: _isExpandedIndex == index, - ); - } - - tabClick (index) { - if (index == this._isExpandedIndex) { - index = -1; - } - setState(() { - this._isExpandedIndex = index; - }); - } - - Widget build(BuildContext context) { - // 实例化语言包 - return Scaffold( - appBar: AppBar( - title: Header.Index( - AppLocalizations.$t('nav_title_0'), - ), - actions: appBarActions(), - ), - body: SingleChildScrollView( - physics: BouncingScrollPhysics(), - // padding: EdgeInsets.all(10), - child: ExpansionPanelList( - animationDuration: Duration(milliseconds: 500), - children: List.generate( - _mapList.length, - (_index) { - return renderPanel(_mapList[_index], _index); - }, - ), - expansionCallback: (index, flag) { - this.tabClick(index); - }, - ), - ), - ); - } -} diff --git a/lib/page/home.dart b/lib/page/home.dart index cd9e83f..0df0f5e 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -2,11 +2,9 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/store/index.dart' show Store; import 'package:efox_flutter/controller/index.dart' as Controller; - -//import 'component/index.dart' as TabIndex; -// import 'mine/index.dart' as MyIndex; +import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'component/tabs.dart' as TabIndex; -import 'mine/index_2.dart' as MyIndex; +import 'mine/index.dart' as MyIndex; import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; @@ -51,11 +49,55 @@ class _IndexState extends State { ); } + renderDrawer() { + return Drawer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + color: Color(AppTheme.mainColor), + ), + padding: const EdgeInsets.only(top: 38.0, bottom: 30), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: ClipOval( + child: Image.asset( + "assets/imgs/avatar.png", + width: 80, + ), + ), + ), + Text( + "Guest", + style: TextStyle(fontWeight: FontWeight.bold), + ) + ], + ), + ), + Expanded( + child: ListView( + children: [ + ListTile( + leading: Icon(Icons.account_circle), + title: Text(AppLocalizations.$t('common.login')), + ), + ], + ), + ), + ], + ), + ); + } + @override Widget build(BuildContext context) { return Store.connect( builder: (context, child, model) { return Scaffold( + drawer: renderDrawer(), bottomNavigationBar: _bottomNavigationBar(model), body: PageView( controller: _pageController, diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index b1266eb..1c53a1a 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -1,7 +1,12 @@ import 'package:flutter/material.dart'; +import 'dart:io' show Platform; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -import 'package:efox_flutter/router/index.dart' show FluroRouter; +//import 'package:efox_flutter/router/index.dart' show FluroRouter; import 'package:efox_flutter/config/theme.dart' show AppTheme; +import 'package:efox_flutter/store/index.dart' show model; +import 'package:efox_flutter/config/color.dart' show materialColor; +import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; +import 'package:efox_flutter/components/expansionTile.dart' as Comp; class _IndexState extends State { @override @@ -9,118 +14,134 @@ class _IndexState extends State { super.initState(); } - List _getList() { - return [ - { - 'name': AppLocalizations.$t('common.changeLanguage'), - 'icon': 59540, // language - 'index': 0 - }, - { - 'name': widget.model.config.state.isPro - ? AppLocalizations.$t('mine.loadLocal') - : AppLocalizations.$t('mine.loadNetwork'), - 'icon': 57539, // import_export - 'index': 2, - }, - { - 'name': AppLocalizations.$t('common.compProgress'), - 'icon': 57709, // low_priority - 'index': 3 - } - ]; - } - - actionsEvent(int index) { - switch (index) { - case 0: - AppLocalizations.changeLanguage(); - break; - case 2: - widget.model.dispatch('config', 'setEnv'); - break; - case 3: - FluroRouter.router.navigateTo( - context, - '/webview?url=${Uri.encodeComponent(widget.model.config.state.env.githubWeb)}&title=${Uri.encodeComponent(AppLocalizations.$t('common.compProgress'))}', + /** + * 国际化 + */ + void openLanguageSelectMenu() async { + await showModalBottomSheet( + context: context, + builder: (BuildContext bc) { + return Container( + child: Wrap( + children: [ + ListTile( + title: Text( + '中文', + ), + onTap: () { + AppLocalizations.changeLanguage(Locale('zh')); + Navigator.pop(context); + }, + ), + ListTile( + title: Text('English'), + onTap: () { + AppLocalizations.changeLanguage(Locale('en')); + Navigator.pop(context); + }, + ), + ], + ), ); - break; - } + }, + ); } @override Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - return NestedScrollView( - headerSliverBuilder: (context, flag) { - return [ - SliverOverlapAbsorber( - handle: - NestedScrollView.sliverOverlapAbsorberHandleFor(context), - child: SliverAppBar( - pinned: true, - expandedHeight: 150, - centerTitle: true, - flexibleSpace: FlexibleSpaceBar( - collapseMode: CollapseMode.pin, - title: Text( - 'Flutter UI', - ), + List _EdageList = []; + materialColor.forEach((k, v) { + _EdageList.add(this.Edage(k, v)); + }); + return Scaffold( + appBar: AppBar( + elevation: 0, + centerTitle: true, + title: Text(AppLocalizations.$t('title_my'))), + body: ListView( + children: [ + ListTile( + onTap: () => this.openLanguageSelectMenu(), + leading: Icon(Icons.language), + title: Text(AppLocalizations.$t('common_mine_1.language')), + trailing: Container( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(AppLocalizations.languageCode), + Icon(Icons.navigate_next) + ], ), - ), + )), + Divider( + color: Color(AppTheme.lineColor), + ), + Comp.ExpansionTile( + leading: Icon(Icons.color_lens), + headerBackgroundColor: Colors.transparent, + title: Row( + children: [ + Text(AppLocalizations.$t('common_mine_1.theme')), + Container( + margin: EdgeInsets.fromLTRB(5, 5, 0, 0), + child: Container( + color: Color(materialColor[model.config.state.theme]), + height: 15, + width: 15, + ), + ) + ], ), - ]; - }, - body: Builder(builder: (context) { - List list = this._getList(); - return CustomScrollView( - slivers: [ - // SliverOverlapInjector与SliverOverlapAbsorber是相对成立的, - // 若不增加SliverOverlapInjector,则下方的list顶部会被上方headerSliverBuilder所创建的组件遮住, - // 增加后,类似clear:both效果,使得布局能顺畅衔接 - SliverOverlapInjector( - // This is the flip side of the SliverOverlapAbsorber above. - handle: - NestedScrollView.sliverOverlapAbsorberHandleFor(context), - ), - SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - dynamic item = list[index]; - if (item['show'] ?? true) { - return Column( - children: [ - ListTile( - onTap: () { - this.actionsEvent(item['index']); - }, - leading: Icon( - IconData( - item['icon'], - fontFamily: 'MaterialIcons', - matchTextDirection: true, - ), - ), - title: Text('${item['name']}'), - ), - Divider( - color: Color(AppTheme.lineColor), - ) - ], - ); - } else { - return Container(); - } - }, - childCount: this._getList().length, + children: [ + Padding( + padding: EdgeInsets.all(10), + child: Wrap( + spacing: 10, + runSpacing: 5, + children: _EdageList, ), - ), + ) ], - ); - }), - ); + ), + Divider( + color: Color(AppTheme.lineColor), + ), + (Platform.isAndroid) + ? ListTile( + onTap: () { + AppVersion().check(context, showTips: true); + }, + leading: Icon(Icons.history), + title: Text(AppLocalizations.$t('common_mine_1.version')), + trailing: Container( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(model.config.state.appVersion), + Icon(Icons.navigate_next) + ], + ), + )) + : Container(), + (Platform.isAndroid) + ? Divider( + color: Color(AppTheme.lineColor), + ) + : Container(), + ], + )); + } + + Widget Edage(name, color) { + return GestureDetector( + onTap: () { + model.dispatch('config', 'setTheme', name); }, + child: Container( + color: Color(color), + height: 30, + width: 30, + ), ); } } diff --git a/lib/page/mine/index_1.dart b/lib/page/mine/index_1.dart deleted file mode 100644 index 32f854d..0000000 --- a/lib/page/mine/index_1.dart +++ /dev/null @@ -1,218 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -import 'package:efox_flutter/router/index.dart' show FluroRouter; -import 'package:efox_flutter/config/theme.dart' show AppTheme; - -class _IndexState extends State { - @override - void initState() { - super.initState(); - } - - List _getList() { - return [ - { - 'name': AppLocalizations.$t('common_mine_1.language'), - 'icon': 59540, // language - 'index': 0 - }, - { - 'name': AppLocalizations.$t('common_mine_1.environment'), - 'icon': 57539, // import_export - 'index': 1, - }, - { - 'name': AppLocalizations.$t('common_mine_1.compProgress'), - 'icon': 57709, // low_priority - 'index': 2 - } - ]; - } - - actionsEvent(int index) { - print('index $index'); - switch (index) { - case 0: - this.openLanguageSelectMenu(); - break; - case 1: - this.openEnvSelectMenu(); - break; - case 2: - FluroRouter.router.navigateTo( - context, - '/webview?url=${Uri.encodeComponent(widget.model.config.state.env.githubWeb)}&title=${Uri.encodeComponent(AppLocalizations.$t('common.compProgress'))}', - ); - break; - } - } - - void pop([message]) { - Navigator.pop(context); - if (message != null) { - Scaffold.of(context).showSnackBar(new SnackBar( - content: new Text(message), - )); - } - } - - /** - * 国际化 - */ - void openLanguageSelectMenu() async { - await showModalBottomSheet( - context: context, - builder: (BuildContext bc) { - return Container( - child: Wrap( - children: [ - ListTile( - //leading: Icon(Icons.label_outline), - title: Text( - AppLocalizations.$t('common_mine_1.cn'), - ), - onTap: () { - AppLocalizations.changeLanguage(Locale('zh')); - this.pop(AppLocalizations.$t('common_mine_1.success')); - }, - ), - ListTile( - //leading: Icon(Icons.label_outline), - title: Text(AppLocalizations.$t('common_mine_1.en')), - onTap: () { - AppLocalizations.changeLanguage(Locale('en')); - this.pop(AppLocalizations.$t('common_mine_1.success')); - }, - ), - ], - ), - ); - }, - ); - } - - /** - * 环境选择 - */ - void openEnvSelectMenu() async { - await showModalBottomSheet( - context: context, - builder: (BuildContext bc) { - return Container( - child: Wrap( - children: [ - ListTile( - //leading: Icon(Icons.label_outline), - title: Text( - AppLocalizations.$t('mine.loadNetwork'), - ), - onTap: () { - widget.model.dispatch('config', 'setEnv', true); - this.pop(AppLocalizations.$t('common_mine_1.success')); - }, - ), - ListTile( - //leading: Icon(Icons.label_outline), - title: Text(AppLocalizations.$t('mine.loadLocal')), - onTap: () { - widget.model.dispatch('config', 'setEnv', false); - this.pop(AppLocalizations.$t('common_mine_1.success')); - }, - ), - ], - ), - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - return SingleChildScrollView( - child: Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(10), - bottomRight: Radius.circular(10), - ), - color: Colors.red, - gradient: LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - colors: [Colors.red, Colors.blue])), - height: 240, - child: Stack( - alignment: const FractionalOffset(0.8, 0.8), - children: [ - Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Opacity( - child: Image.network( - 'https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png', - width: 80.0, - height: 80.0, - fit: BoxFit.cover, - ), - opacity: 1, - ), - SizedBox( - height: 10, - ), - Text( - "Flutter-UI", - style: TextStyle( - color: Colors.white, - fontSize: 26, - fontWeight: FontWeight.bold), - ) - ], - ), - ) - ], - ), - ), - ListView.builder( - shrinkWrap: true, - itemCount: _getList().length * 2, - itemBuilder: (context, index) { - double _index = index / 2; - if (index % 2 == 0) { - dynamic item = _getList()[_index.toInt()]; - return ListTile( - onTap: () { - actionsEvent(item['index']); - }, - leading: Icon( - IconData( - item['icon'], - fontFamily: 'MaterialIcons', - matchTextDirection: true, - ), - ), - title: Text(item['name']), - ); - } else { - return Divider( - color: Color(AppTheme.lineColor), - ); - } - }, - ), - ], - ), - ); - } -} - -class Index extends StatefulWidget { - final dynamic model; - - Index({Key key, this.model}) : super(key: key); - - @override - _IndexState createState() => _IndexState(); -} diff --git a/lib/page/mine/index_2.dart b/lib/page/mine/index_2.dart deleted file mode 100644 index ca75f0c..0000000 --- a/lib/page/mine/index_2.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'package:flutter/material.dart'; -import 'dart:io' show Platform; -import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -//import 'package:efox_flutter/router/index.dart' show FluroRouter; -import 'package:efox_flutter/config/theme.dart' show AppTheme; -import 'package:efox_flutter/store/index.dart' show model; -import 'package:efox_flutter/config/color.dart' show materialColor; -import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; -import 'package:efox_flutter/components/expansionTile.dart' as Comp; - -class _IndexState extends State { - @override - void initState() { - super.initState(); - } - - /** - * 国际化 - */ - void openLanguageSelectMenu() async { - await showModalBottomSheet( - context: context, - builder: (BuildContext bc) { - return Container( - child: Wrap( - children: [ - ListTile( - title: Text( - '中文', - ), - onTap: () { - AppLocalizations.changeLanguage(Locale('zh')); - Navigator.pop(context); - }, - ), - ListTile( - title: Text('English'), - onTap: () { - AppLocalizations.changeLanguage(Locale('en')); - Navigator.pop(context); - }, - ), - ], - ), - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - List _EdageList = []; - materialColor.forEach((k, v) { - _EdageList.add(this.Edage(k, v)); - }); - return Scaffold( - appBar: AppBar( - elevation: 0, - centerTitle: true, - title: Text(AppLocalizations.$t('nav_title_1'))), - body: ListView( - children: [ - ListTile( - onTap: () => this.openLanguageSelectMenu(), - leading: Icon(Icons.language), - title: Text(AppLocalizations.$t('common_mine_1.language')), - trailing: Container( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(AppLocalizations.languageCode), - Icon(Icons.navigate_next) - ], - ), - )), - Divider( - color: Color(AppTheme.lineColor), - ), - Comp.ExpansionTile( - leading: Icon(Icons.color_lens), - headerBackgroundColor: Colors.transparent, - title: Row( - children: [ - Text(AppLocalizations.$t('common_mine_1.theme')), - Container( - margin: EdgeInsets.fromLTRB(5, 5, 0, 0), - child: Container( - color: Color(materialColor[model.config.state.theme]), - height: 15, - width: 15, - ), - ) - ], - ), - children: [ - Padding( - padding: EdgeInsets.all(10), - child: Wrap( - spacing: 10, - runSpacing: 5, - children: _EdageList, - ), - ) - ], - ), - Divider( - color: Color(AppTheme.lineColor), - ), - (Platform.isAndroid) - ? ListTile( - onTap: () { - AppVersion().check(context, showTips: true); - }, - leading: Icon(Icons.history), - title: Text(AppLocalizations.$t('common_mine_1.version')), - trailing: Container( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(model.config.state.appVersion), - Icon(Icons.navigate_next) - ], - ), - )) - : Container(), - (Platform.isAndroid) - ? Divider( - color: Color(AppTheme.lineColor), - ) - : Container(), - ], - )); - } - - Widget Edage(name, color) { - return GestureDetector( - onTap: () { - model.dispatch('config', 'setTheme', name); - }, - child: Container( - color: Color(color), - height: 30, - width: 30, - ), - ); - } -} - -class Index extends StatefulWidget { - final dynamic model; - - Index({Key key, this.model}) : super(key: key); - - @override - _IndexState createState() => _IndexState(); -} diff --git a/lib/store/models/author_state_model.dart b/lib/store/models/author_state_model.dart new file mode 100644 index 0000000..5a6df85 --- /dev/null +++ b/lib/store/models/author_state_model.dart @@ -0,0 +1,38 @@ +import '../objects/author_info.dart' show AuthorInfo; + +class AuthorModel { + get state => { + 'ken': AuthorInfo( + name: 'Ken', + avatarUrl: + 'https://avatars0.githubusercontent.com/u/3890513?s=400&v=4', + id: 'ken', + url: 'https://github.com/ckken', + ), + 'wanwu': AuthorInfo( + name: 'wanwu', + avatarUrl: + 'https://avatars3.githubusercontent.com/u/15372930?s=460&v=4', + id: 'wanwu', + url: 'https://github.com/wanwusangzhi', + ), + 'lhr': AuthorInfo( + name: 'Lin-Haoran', + avatarUrl: + 'https://avatars3.githubusercontent.com/u/30428314?s=400&v=4', + id: 'lhr', + url: 'https://github.com/DIVINER-only', + ) + }; + + getAuthorInfo(key) { + return this.state[key]; + } + + methods(name, payload) async { + switch (name) { + case 'getAuthorInfo': + return getAuthorInfo(payload); + } + } +} diff --git a/lib/store/models/main_state_model.dart b/lib/store/models/main_state_model.dart index dc1ec67..2de6533 100644 --- a/lib/store/models/main_state_model.dart +++ b/lib/store/models/main_state_model.dart @@ -1,6 +1,7 @@ import 'package:scoped_model/scoped_model.dart'; import 'user_model.dart' show UserModel; import 'config_state_model.dart' show ConfigModel; +import 'author_state_model.dart' show AuthorModel; /** * get state: model.modelName.state.xxx @@ -14,12 +15,14 @@ import 'config_state_model.dart' show ConfigModel; class MainStateModel extends Model with UserModel { Map state = {}; ConfigModel config = ConfigModel(); + AuthorModel author = AuthorModel(); MainStateModel() { // 初始化实例数据 // order for dispatch to get destination model's methods this.state = { 'config': config, + 'author': author }; //init model data config.getAppVersion(); diff --git a/lib/store/objects/author_info.dart b/lib/store/objects/author_info.dart new file mode 100644 index 0000000..d90f8db --- /dev/null +++ b/lib/store/objects/author_info.dart @@ -0,0 +1,8 @@ +class AuthorInfo extends Object { + String name; + String avatarUrl; + String url; + String id; + + AuthorInfo({this.name, this.avatarUrl, this.id, this.url}); +} diff --git a/lib/widget/author_list.dart b/lib/widget/author_list.dart new file mode 100644 index 0000000..eb112e2 --- /dev/null +++ b/lib/widget/author_list.dart @@ -0,0 +1,6 @@ +Map list = { + 'AnimatedBuilder': 'lhr', + 'GridView': 'wanwu', + 'ListView': 'lhr', + 'NestedScrollView': 'ken' +}; diff --git a/lib/widget/gestures/gesturedetector/demo_force_press.dart b/lib/widget/gestures/gesturedetector/demo_force_press.dart index 3796dec..d02a224 100644 --- a/lib/widget/gestures/gesturedetector/demo_force_press.dart +++ b/lib/widget/gestures/gesturedetector/demo_force_press.dart @@ -38,36 +38,36 @@ class _IndexState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text("使用ForcePress相关属性将不会触发Tap属性"), - GestureDetector( - onForcePressEnd: (ForcePressDetails ev) { - updateText('onForcePressEnd ${ev} ${ev.globalPosition}'); - }, - onForcePressStart: (ForcePressDetails ev) { - updateText('onForcePressStart ${ev} ${ev.globalPosition}'); - }, - onForcePressUpdate: (ForcePressDetails ev) { - updateText('onForcePressUpdate ${ev} ${ev.globalPosition}'); - }, - onForcePressPeak: (ForcePressDetails ev) { - updateText('onForcePressPeak ${ev} ${ev.globalPosition}'); - }, - child: RandomContainer( - height: 100, - width: 200, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Use onForcePressXX properties and Tap or DoubleTap are not useful'), - ], - ), - ), - ), - Divider( - height: 10, - ), - Text(_value1), + // Text("使用ForcePress相关属性将不会触发Tap属性"), + // GestureDetector( + // onForcePressEnd: (ForcePressDetails ev) { + // updateText('onForcePressEnd ${ev} ${ev.globalPosition}'); + // }, + // onForcePressStart: (ForcePressDetails ev) { + // updateText('onForcePressStart ${ev} ${ev.globalPosition}'); + // }, + // onForcePressUpdate: (ForcePressDetails ev) { + // updateText('onForcePressUpdate ${ev} ${ev.globalPosition}'); + // }, + // onForcePressPeak: (ForcePressDetails ev) { + // updateText('onForcePressPeak ${ev} ${ev.globalPosition}'); + // }, + // child: RandomContainer( + // height: 100, + // width: 200, + // child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text( + // 'Use onForcePressXX properties and Tap or DoubleTap are not useful'), + // ], + // ), + // ), + // ), + // Divider( + // height: 10, + // ), + // Text(_value1), Divider( height: 20, ), diff --git a/lib/widget/gestures/gesturedetector/demo_scale.dart b/lib/widget/gestures/gesturedetector/demo_scale.dart index 9b8843b..cebb264 100644 --- a/lib/widget/gestures/gesturedetector/demo_scale.dart +++ b/lib/widget/gestures/gesturedetector/demo_scale.dart @@ -16,18 +16,13 @@ class _IndexState extends State { automaticallyImplyLeading: false, ), body: Center( - child: RandomContainer( - changeOnRedraw: true, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + // Image.asset('assets/imgs/avatar.png'), Text("手势操作图片放大或缩小"), - Divider( - height: 10, - ), GestureDetector( - child: Image.network( - 'https://avatars3.githubusercontent.com/u/15372930?s=460&v=4', + child: Image.asset('assets/imgs/avatar.png', fit: BoxFit.contain, width: _width, ), @@ -46,7 +41,6 @@ class _IndexState extends State { ) ], ), - ), ), ); } diff --git a/locale/en.json b/locale/en.json index 46c2ff3..92e5916 100644 --- a/locale/en.json +++ b/locale/en.json @@ -1,15 +1,9 @@ { - "nav_title_0": "Components", - "nav_title_1": "My", "title_component": "Components", "title_my": "My", - "widgetType": { - "regularLayout": "common layout" - }, "common": { - "changeLanguage": "显示中文", - "changeVersion": "checkVersion", - "compProgress": "Components Progress" + "login": "登录", + "logout": "退出" }, "common_mine_1": { "cn": "CN", diff --git a/locale/zh.json b/locale/zh.json index 8a782c6..f2a61b9 100644 --- a/locale/zh.json +++ b/locale/zh.json @@ -1,15 +1,9 @@ { - "nav_title_0": "组件", - "nav_title_1": "我的", "title_component": "组件", "title_my": "我的", - "widgetType": { - "regularLayout": "常规布局" - }, "common": { - "changeLanguage": "Switch to English", - "changeVersion": "更新版本", - "compProgress": "组件进度" + "login": "登录", + "logout": "退出" }, "common_mine_1": { "cn": "中文", diff --git a/readme/widget_progress.md b/readme/widget_progress.md index a107eaa..80cc90e 100644 --- a/readme/widget_progress.md +++ b/readme/widget_progress.md @@ -81,7 +81,7 @@ Flutter UI │ ├─absorbpointer │ ├─dismissible │ ├─dragtarget - │ ├─gesturedetector + │ ├─gesturedetector 【✔️ v1.0】 │ ├─ignorepointer │ └─longpressdraggable 【✔️ v1.0】 ├─navigator From 9953ae45716024e79de326e4f0c496d214b36936 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Mon, 25 Mar 2019 22:32:52 +0800 Subject: [PATCH 040/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/app-login/index.dart | 95 +++++++++++++++++++ lib/page/home.dart | 16 +++- lib/store/http.dart | 22 +++-- lib/store/models/author_state_model.dart | 51 +++++----- lib/store/models/main_state_model.dart | 6 +- lib/store/models/user_model.dart | 52 ++++++++-- lib/store/objects/user_info.dart | 2 +- .../gesturedetector/demo_force_press.dart | 14 +-- 8 files changed, 198 insertions(+), 60 deletions(-) create mode 100644 lib/page/app-login/index.dart diff --git a/lib/page/app-login/index.dart b/lib/page/app-login/index.dart new file mode 100644 index 0000000..a799b44 --- /dev/null +++ b/lib/page/app-login/index.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:efox_flutter/store/models/main_state_model.dart' + show MainStateModel; +import 'package:efox_flutter/lang/index.dart' show AppLocalizations; + +class Index extends StatefulWidget { + MainStateModel model; + + Index({Key key, @required this.model}) : super(key: key); + + @override + _IndexState createState() => _IndexState(); +} + +class _IndexState extends State { + TextEditingController nameCtl = TextEditingController(text: ''); + TextEditingController pwdCtl = TextEditingController(text: ''); + + GlobalKey _formKey = GlobalKey(); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: Text( + AppLocalizations.$t('common.login'), + textAlign: TextAlign.center, + ), + automaticallyImplyLeading: false, + ), + body: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 50, horizontal: 24), + child: Form( + key: _formKey, + autovalidate: true, + child: Column( + children: [ + TextFormField( + controller: nameCtl, + autofocus: true, + decoration: InputDecoration( + labelText: '账户名', + hintText: '请输入Github账户名', + icon: Icon(Icons.person), + ), + validator: (v) { + return v.trim().length > 0 ? null : '用户名不能为空'; + }, + ), + TextFormField( + controller: pwdCtl, + decoration: InputDecoration( + labelText: '密码', + hintText: '请输入登录密码', + icon: Icon(Icons.lock), + ), + obscureText: true, + validator: (v) { + return v.trim().length > 5 ? null : "密码不能少于6位"; + }, + ), + Padding( + padding: EdgeInsets.only(top: 50), + child: Row( + children: [ + Expanded( + child: RaisedButton( + padding: EdgeInsets.all(15), + color: Theme.of(context).primaryColor, + textColor: + Theme.of(context).primaryTextTheme.title.color, + child: Text( + AppLocalizations.$t('common.login'), + ), + onPressed: () { + if ((_formKey.currentState as FormState) + .validate()) { + widget.model.dispatch('user', 'login', + {'name': nameCtl.text, 'pwd': pwdCtl.text}); + } + }, + ), + ) + ], + ), + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/page/home.dart b/lib/page/home.dart index 0df0f5e..80e585d 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -5,6 +5,7 @@ import 'package:efox_flutter/controller/index.dart' as Controller; import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'component/tabs.dart' as TabIndex; import 'mine/index.dart' as MyIndex; +import 'app-login/index.dart' as LoginIndex; import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; @@ -16,6 +17,7 @@ class Index extends StatefulWidget { class _IndexState extends State { int _currentIndex = 0; PageController _pageController; + dynamic model; @override void initState() { @@ -55,14 +57,14 @@ class _IndexState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( + height: 200, decoration: BoxDecoration( color: Color(AppTheme.mainColor), ), - padding: const EdgeInsets.only(top: 38.0, bottom: 30), child: Row( children: [ Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), + padding: EdgeInsets.symmetric(horizontal: 20.0), child: ClipOval( child: Image.asset( "assets/imgs/avatar.png", @@ -71,7 +73,7 @@ class _IndexState extends State { ), ), Text( - "Guest", + this.model.user.state.name ?? 'Guest', style: TextStyle(fontWeight: FontWeight.bold), ) ], @@ -83,6 +85,13 @@ class _IndexState extends State { ListTile( leading: Icon(Icons.account_circle), title: Text(AppLocalizations.$t('common.login')), + onTap: () { + this.model.dispatch('user', 'setUser', {'name': 'Guest'}); + Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext context) { + return LoginIndex.Index(model: this.model,); + })); + }, ), ], ), @@ -96,6 +105,7 @@ class _IndexState extends State { Widget build(BuildContext context) { return Store.connect( builder: (context, child, model) { + this.model = model; return Scaffold( drawer: renderDrawer(), bottomNavigationBar: _bottomNavigationBar(model), diff --git a/lib/store/http.dart b/lib/store/http.dart index 04f73c5..cc40737 100644 --- a/lib/store/http.dart +++ b/lib/store/http.dart @@ -1,12 +1,13 @@ -import 'package:dio/dio.dart' - show Dio, Options, LogInterceptor, DioError, BaseOptions; +import 'package:dio/dio.dart' show Dio, Options, DioError, BaseOptions; Dio getDio([Options options]) { - Dio dio = new Dio(BaseOptions( - connectTimeout: 30 * 1000, - receiveTimeout: 30 * 1000, - )); // with default Options - // dio.interceptors.add(LogInterceptor(responseBody: true)); + Dio dio = new Dio( + BaseOptions( + connectTimeout: 30 * 1000, + receiveTimeout: 30 * 1000, + headers: options.headers, + ), + ); // with default Options return dio; } @@ -20,6 +21,9 @@ Future get(url, [data = const {}]) async { } } -Future post(url, [data = const {}, params = const {}]) async { - return getDio().post(url, data: data, queryParameters: params); +Future post(url, [data = const {}, params = const {}, Options options]) async { + return getDio(options).post( + url, + data: data + ); } diff --git a/lib/store/models/author_state_model.dart b/lib/store/models/author_state_model.dart index 5a6df85..214d90b 100644 --- a/lib/store/models/author_state_model.dart +++ b/lib/store/models/author_state_model.dart @@ -2,37 +2,32 @@ import '../objects/author_info.dart' show AuthorInfo; class AuthorModel { get state => { - 'ken': AuthorInfo( - name: 'Ken', - avatarUrl: - 'https://avatars0.githubusercontent.com/u/3890513?s=400&v=4', - id: 'ken', - url: 'https://github.com/ckken', - ), - 'wanwu': AuthorInfo( - name: 'wanwu', - avatarUrl: - 'https://avatars3.githubusercontent.com/u/15372930?s=460&v=4', - id: 'wanwu', - url: 'https://github.com/wanwusangzhi', - ), - 'lhr': AuthorInfo( - name: 'Lin-Haoran', - avatarUrl: - 'https://avatars3.githubusercontent.com/u/30428314?s=400&v=4', - id: 'lhr', - url: 'https://github.com/DIVINER-only', - ) - }; - - getAuthorInfo(key) { - return this.state[key]; - } + 'ken': AuthorInfo( + name: 'Ken', + avatarUrl: + 'https://avatars0.githubusercontent.com/u/3890513?s=400&v=4', + id: 'ken', + url: 'https://github.com/ckken', + ), + 'wanwu': AuthorInfo( + name: 'wanwu', + avatarUrl: + 'https://avatars3.githubusercontent.com/u/15372930?s=460&v=4', + id: 'wanwu', + url: 'https://github.com/wanwusangzhi', + ), + 'lhr': AuthorInfo( + name: 'Lin-Haoran', + avatarUrl: + 'https://avatars3.githubusercontent.com/u/30428314?s=400&v=4', + id: 'lhr', + url: 'https://github.com/DIVINER-only', + ), + }; methods(name, payload) async { switch (name) { - case 'getAuthorInfo': - return getAuthorInfo(payload); + case 'setAuthorInfo': break; } } } diff --git a/lib/store/models/main_state_model.dart b/lib/store/models/main_state_model.dart index 2de6533..1b1a5d0 100644 --- a/lib/store/models/main_state_model.dart +++ b/lib/store/models/main_state_model.dart @@ -12,17 +12,19 @@ import 'author_state_model.dart' show AuthorModel; */ ///主数据模型,需要全局使用的数据在这里添加模型 -class MainStateModel extends Model with UserModel { +class MainStateModel extends Model { Map state = {}; ConfigModel config = ConfigModel(); AuthorModel author = AuthorModel(); + UserModel user = UserModel(); MainStateModel() { // 初始化实例数据 // order for dispatch to get destination model's methods this.state = { 'config': config, - 'author': author + 'author': author, + 'user': user }; //init model data config.getAppVersion(); diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index 1a5880f..952170a 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -1,17 +1,49 @@ -import 'package:scoped_model/scoped_model.dart'; -import '../objects/user_info.dart'; +import 'package:dio/dio.dart' show Options; +import '../objects/user_info.dart' show UserInfo; +import 'package:efox_flutter/store/http.dart' as Http; +import 'package:dio/dio.dart' show Dio, BaseOptions, Options; +import 'dart:convert'; -mixin UserModel on Model { +Map Url = {'login': 'https://api.github.com/authorizations'}; + +class UserModel extends Object { UserInfo _userInfo = UserInfo(); - get userInfo => _userInfo; + get state => _userInfo; + + setUser(payload) { + _userInfo = UserInfo(name: payload['name']); + } - Future getAction() async { - _userInfo.loading = true; - notifyListeners(); + Future login(payload) async { + var name = payload['name']; + var pwd = payload['pwd']; + var bytes = utf8.encode("$name:$pwd"); + var credentials = base64.encode(bytes); + var response = await Http.post( + Url['login'], + { + "scopes": ["user", "repo", "gist", "notifications", "public_repo"], + "note": "admin_script", + }, + {}, + Options(headers: {'Authorization': 'Basic $credentials'})).then((res) { + print('success ==$res'); + }).catchError((err) { + print('error -------------- ${err.response}'); + print('error -------------- ${err.request.data}'); + print('error -------------- ${err.request.headers}'); + }); + print('response =---------------------'); + print(response); } - void setAge() { - _userInfo.age += 1; - notifyListeners(); + methods(name, payload) { + switch (name) { + case 'setUser': + setUser(payload); + break; + case 'login': + login(payload); + } } } diff --git a/lib/store/objects/user_info.dart b/lib/store/objects/user_info.dart index 97b962e..cf7403f 100644 --- a/lib/store/objects/user_info.dart +++ b/lib/store/objects/user_info.dart @@ -1,4 +1,4 @@ -class UserInfo extends Object{ +class UserInfo extends Object { String fbid; String uid; String name; diff --git a/lib/widget/gestures/gesturedetector/demo_force_press.dart b/lib/widget/gestures/gesturedetector/demo_force_press.dart index d02a224..974e2f5 100644 --- a/lib/widget/gestures/gesturedetector/demo_force_press.dart +++ b/lib/widget/gestures/gesturedetector/demo_force_press.dart @@ -7,19 +7,19 @@ class Index extends StatefulWidget { } class _IndexState extends State { - String _value1 = ''; + // String _value1 = ''; String _value2 = ''; @override void initState() { super.initState(); } - updateText(txt) { - print(txt); - setState(() { - _value1 = txt; - }); - } + // updateText(txt) { + // print(txt); + // setState(() { + // _value1 = txt; + // }); + // } updateText2(txt) { print(txt); From 33277b65162a6eda61cdd3a0546aa50addce6630 Mon Sep 17 00:00:00 2001 From: doerme <398921432@qq.com> Date: Tue, 26 Mar 2019 00:15:16 +0800 Subject: [PATCH 041/100] =?UTF-8?q?docs:=20=E5=A2=9E=E5=8A=A0gp=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 6 ++++++ readme/google_play.png | Bin 0 -> 13957 bytes 2 files changed, 6 insertions(+) create mode 100644 readme/google_play.png diff --git a/readme.md b/readme.md index 171da2a..25776e9 100644 --- a/readme.md +++ b/readme.md @@ -3,6 +3,12 @@ +## google play 下载 + + + git it on google play + + ## 功能清单 + widget 组件教程 + 多语言切换 diff --git a/readme/google_play.png b/readme/google_play.png new file mode 100644 index 0000000000000000000000000000000000000000..c77b7464b049c7d6666155c61df5f486f241f760 GIT binary patch literal 13957 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 X%LPP+756@c3` Date: Tue, 26 Mar 2019 00:16:30 +0800 Subject: [PATCH 042/100] =?UTF-8?q?docs:=20gp=E5=9B=BE=E7=89=87=E5=AE=BD?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 25776e9..163cef8 100644 --- a/readme.md +++ b/readme.md @@ -6,7 +6,7 @@ ## google play 下载 - git it on google play + git it on google play ## 功能清单 From a17e35c7ceabe8b7a3ef08f4dccafc0ee9181a6f Mon Sep 17 00:00:00 2001 From: doerme <398921432@qq.com> Date: Tue, 26 Mar 2019 00:17:26 +0800 Subject: [PATCH 043/100] docs: gp target blank --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 163cef8..9a684f5 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,7 @@ ## google play 下载 - + git it on google play From 77e0a025f3390de149d35564a6f36c6d322a4e1b Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 26 Mar 2019 19:48:18 +0800 Subject: [PATCH 044/100] =?UTF-8?q?=E5=A2=9E=E5=8A=A0privide=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/store/provide.dart | 73 +++++++++++++++++++++++ lib/store/provide_model/config_model.dart | 67 +++++++++++++++++++++ lib/store/provide_model/user_model.dart | 17 ++++++ pubspec.yaml | 1 + 4 files changed, 158 insertions(+) create mode 100644 lib/store/provide.dart create mode 100644 lib/store/provide_model/config_model.dart create mode 100644 lib/store/provide_model/user_model.dart diff --git a/lib/store/provide.dart b/lib/store/provide.dart new file mode 100644 index 0000000..223b440 --- /dev/null +++ b/lib/store/provide.dart @@ -0,0 +1,73 @@ +import './provide_model/user_model.dart' show UserModel; +import './provide_model/config_model.dart' show ConfigModel; + +import 'package:provide/provide.dart' + show + Provider, + Provide, + ProviderNode, + Providers, + ProvideMulti, + ProviderScope; +import 'package:flutter/material.dart' show StreamBuilder; + +/** + * import 'package:efox_flutter/store/provide.dart' as Store + * Store.userModel + * Store.configModel + */ +class Store { + static init({model, child, dispose = true}) { + Providers providers = Providers(); + UserModel userModel = UserModel(); + ConfigModel configModel = ConfigModel(); + providers + ..provide(Provider.value(userModel)) + ..provide(Provider.value(configModel)); + return ProviderNode( + child: child, + providers: providers, + dispose: dispose, + ); + } + + /** + * 获取 + */ + static get(context, {scope}) => Provide.value(context, scope: scope); + /** + * 监听 + */ + static connect({builder, child, scope}) { + return Provide( + builder: builder, + child: child, + scope: scope, + ); + } + + /** + * 通过流的方式 监听 + */ + static stream({builder, model, context}) { + return StreamBuilder( + initialData: model, + stream: Provide.stream(context), + builder: builder); + } + + /** + * 链接多个类型 + */ + static pm( + {builder, + child, + List requestedValues, + Map> requestedScopedValues}) { + return ProvideMulti( + builder: builder, + child: child, + requestedValues: requestedValues, + requestedScopedValues: requestedScopedValues); + } +} diff --git a/lib/store/provide_model/config_model.dart b/lib/store/provide_model/config_model.dart new file mode 100644 index 0000000..0679617 --- /dev/null +++ b/lib/store/provide_model/config_model.dart @@ -0,0 +1,67 @@ +import 'dart:convert'; +import 'package:efox_flutter/config/index.dart' as Config; +import 'package:efox_flutter/store/index.dart' show model; +import 'package:efox_flutter/utils/loadAsset.dart' show loadAssets; +import 'package:efox_flutter/utils/localstage.dart' show LocalStorage; +import 'package:package_info/package_info.dart' show PackageInfo; +import 'package:flutter/material.dart' show ChangeNotifier; + +class ConfigInfo { + bool isPro = Config.isPro; + String version = '1.0'; + dynamic env = Config.env; + String theme = 'red'; + String appVersion = '-'; +} + +ConfigInfo _appConfigInfo = new ConfigInfo(); + +class ConfigModel extends ChangeNotifier { + get state => _appConfigInfo; + + Future getAppVersion() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + _appConfigInfo.appVersion = await packageInfo.version; + } + + Future getTheme() async { + String theme = await LocalStorage.get('theme'); + if (theme != null) { + _appConfigInfo.theme = theme; + } + } + + dynamic getVersion() async { + print('version ${model.config.state.env.versionUrl}'); + String _version = + await loadAssets(model.config.state.env.versionUrl).then((resp) { + Map res = json.decode(resp); + return res['version'].toString() ?? '0.1'; + }).catchError((err) { + print('err $err'); + return '0.0'; + }); + print('_version ${_version}'); + return _version; + } + + methods(name, payload) async { + print('payload= $payload'); + + switch (name) { + case 'setEnv': + _appConfigInfo.isPro = payload; + break; + case 'setVersion': + _appConfigInfo.version = await this.getVersion(); + break; + case 'setTheme': + _appConfigInfo.theme = payload; + LocalStorage.set('theme', payload); + break; + case 'getTheme': + await this.getTheme(); + break; + } + } +} diff --git a/lib/store/provide_model/user_model.dart b/lib/store/provide_model/user_model.dart new file mode 100644 index 0000000..b0391f5 --- /dev/null +++ b/lib/store/provide_model/user_model.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import '../objects/user_info.dart'; + +class UserModel extends ChangeNotifier { + UserInfo _userInfo = UserInfo(); + get userInfo => _userInfo; + + Future getAction() async { + _userInfo.loading = true; + notifyListeners(); + } + + void setAge() { + _userInfo.age += 1; + notifyListeners(); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 44564e6..3f58a0f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: pull_to_refresh: ^1.1.6 #加载更多 shared_preferences: ^0.4.2 #简单数据存储 scoped_model: ^1.0.1 + provide: ^1.0.2 dio: ^2.0.14 cached_network_image: ^0.5.1 intl: ^0.15.7 From 337b1d87c481b0330249c99a9f71b10f53af287f Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 26 Mar 2019 19:55:00 +0800 Subject: [PATCH 045/100] =?UTF-8?q?feat:=E5=AF=BC=E5=87=BA=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=85=A8=E5=B1=80=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/store/provide.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/store/provide.dart b/lib/store/provide.dart index 223b440..ee12f70 100644 --- a/lib/store/provide.dart +++ b/lib/store/provide.dart @@ -1,6 +1,5 @@ import './provide_model/user_model.dart' show UserModel; import './provide_model/config_model.dart' show ConfigModel; - import 'package:provide/provide.dart' show Provider, @@ -11,6 +10,10 @@ import 'package:provide/provide.dart' ProviderScope; import 'package:flutter/material.dart' show StreamBuilder; +//导出类型 以便全局调用 +export './provide_model/user_model.dart'; +export './provide_model/config_model.dart'; + /** * import 'package:efox_flutter/store/provide.dart' as Store * Store.userModel From 058a8e0cdd21ed3234f24567784c08f05aba60a3 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Tue, 26 Mar 2019 22:11:51 +0800 Subject: [PATCH 046/100] =?UTF-8?q?feat:=E4=BC=98=E5=8C=96=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=B1=82=E4=B8=BAprovide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- analysis_options.yaml | 6 +- lib/components/baseComp.dart | 51 ----------- lib/components/exampleComp.dart | 29 +++--- lib/components/headerComp.dart | 15 ---- lib/components/markdownComp.dart | 3 - lib/components/widgetComp.dart | 90 ++++++------------- lib/controller/index.dart | 5 +- lib/main.dart | 23 ++--- lib/page/app-login/index.dart | 9 +- lib/page/component/tabs.dart | 5 +- lib/page/home.dart | 51 +++++------ lib/page/mine/index.dart | 18 ++-- lib/router/index.dart | 4 + lib/store/http.dart | 54 ++++++++--- lib/store/index.dart | 64 ++++++++----- lib/store/models/author_state_model.dart | 52 +++++------ lib/store/models/config_state_model.dart | 50 ++++------- lib/store/models/main_state_model.dart | 44 --------- lib/store/models/user_model.dart | 19 +--- lib/utils/appVersion.dart | 2 - lib/utils/file.dart | 14 ++- lib/utils/loadAsset.dart | 19 ++-- .../gestures/gesturedetector/demo_scale.dart | 1 - pubspec.yaml | 2 +- 24 files changed, 241 insertions(+), 389 deletions(-) delete mode 100644 lib/components/baseComp.dart delete mode 100644 lib/components/headerComp.dart delete mode 100644 lib/store/models/main_state_model.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index 5eacbca..077bb34 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,3 +1,3 @@ -analyzer: - errors: - mixin_inherits_from_not_object: ignore \ No newline at end of file +# analyzer: +# errors: +# mixin_inherits_from_not_object: ignore \ No newline at end of file diff --git a/lib/components/baseComp.dart b/lib/components/baseComp.dart deleted file mode 100644 index 95c55b0..0000000 --- a/lib/components/baseComp.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:efox_flutter/store/index.dart' show Store; -import 'headerComp.dart' as Header; - -class Index extends StatelessWidget { - final dynamic child; - final String title; - - Index({Key key, this.title, this.child}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Store.connect(builder: (context, child, model) { - return Scaffold( - appBar: AppBar( - title: Header.Index(this.title), - ), - body: ListView( - children: [this.child(context, child, model)], - ), - ); - }); - } -} - -//import 'package:flutter/material.dart'; -//import 'package:efox_flutter/page/Index.dart'; -// -//class TestDemo extends StatefulWidget { -// @override -// _TestDemoState createState() => new _TestDemoState(); -//} -// -//class _TestDemoState extends State { -// @override -// Widget build(BuildContext context) { -// return Index( -// title: 'TestDemo', -// child: (context, child, model) { -// return Center( -// child: Text( -// 'Test title', -// style: TextStyle( -// fontSize: 22.0, -// ), -// ), -// ); -// }, -// ); -// } -//} diff --git a/lib/components/exampleComp.dart b/lib/components/exampleComp.dart index 314a926..baf6d96 100644 --- a/lib/components/exampleComp.dart +++ b/lib/components/exampleComp.dart @@ -1,7 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/store/models/main_state_model.dart' - show MainStateModel; -import 'package:efox_flutter/store/index.dart' show Store; import 'package:efox_flutter/config/theme.dart' show AppTheme; class Index extends StatelessWidget { @@ -12,21 +9,17 @@ class Index extends StatelessWidget { @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; - return Store.connect( - builder: (context, child, MainStateModel model) { - return Center( - child: Container( - margin: EdgeInsets.all(10), - decoration: BoxDecoration( - border: Border.all(color: Color(AppTheme.mainColor), width: 1.0), - ), - child: SizedBox.fromSize( - size: size / 1.3, - child: this.child, - ), - ), - ); - }, + return Center( + child: Container( + margin: EdgeInsets.all(10), + decoration: BoxDecoration( + border: Border.all(color: Color(AppTheme.mainColor), width: 1.0), + ), + child: SizedBox.fromSize( + size: size / 1.3, + child: this.child, + ), + ), ); } } diff --git a/lib/components/headerComp.dart b/lib/components/headerComp.dart deleted file mode 100644 index f756d18..0000000 --- a/lib/components/headerComp.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class Index extends StatelessWidget { - final String text; - Index(this.text); - @override - Widget build(BuildContext context) { - return Text( - this.text, - style: TextStyle( - //color: Theme.of(context).primaryTextTheme.title.color - ), - ); - } -} diff --git a/lib/components/markdownComp.dart b/lib/components/markdownComp.dart index a2e2508..ae61332 100644 --- a/lib/components/markdownComp.dart +++ b/lib/components/markdownComp.dart @@ -2,9 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart' as md; import 'package:efox_flutter/utils/syntaxHighlighter.dart' show DartSyntaxHighlighter; -import 'package:efox_flutter/config/color.dart' show materialColor; -import 'package:efox_flutter/config/theme.dart' show AppTheme; -import 'package:efox_flutter/store/index.dart' show model; class Index extends StatelessWidget { final String data; diff --git a/lib/components/widgetComp.dart b/lib/components/widgetComp.dart index dcb2f66..95b5d67 100644 --- a/lib/components/widgetComp.dart +++ b/lib/components/widgetComp.dart @@ -1,17 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/store/index.dart' show Store; import 'package:efox_flutter/components/markdownComp.dart' as MarkDownComp; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -import 'package:efox_flutter/components/baseComp.dart' as BaseComp; import 'package:efox_flutter/components/exampleComp.dart' as ExampleComp; import 'package:efox_flutter/components/updatingComp.dart' as UpdatingComp; import 'package:efox_flutter/utils/file.dart' as FileUtils; -import 'package:efox_flutter/utils/loadAsset.dart' as LoadAssetUtils; import 'package:efox_flutter/router/index.dart' show FluroRouter; import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'package:efox_flutter/utils/share.dart' as AppShare; import 'package:efox_flutter/widget/author_list.dart' as AuthorList; import 'package:efox_flutter/store/objects/author_info.dart' show AuthorInfo; +import 'package:efox_flutter/store/index.dart' show AuthorModel, ConfigModel, Provide; class Index extends StatefulWidget { final List demoChild; @@ -42,12 +40,11 @@ class IndexState extends State { } authorTile(nameKey) { - AuthorInfo info = this.model.author.state[nameKey]; + AuthorInfo info = Provide.value(context).list[nameKey]; return Container( child: ListTile( onTap: () { - FluroRouter.router.navigateTo(context, - '/webview?title=${'GitHub-' + info.name}&url=${Uri.encodeComponent(info.url)}'); + FluroRouter.webview(context: context, title: 'GitHub-' + info.name, url: Uri.encodeComponent(info.url)); }, leading: CircleAvatar( backgroundImage: NetworkImage( @@ -78,12 +75,11 @@ class IndexState extends State { this._bodyList.length = 0; String mdText = await this.getMdFile(widget.mdUrl); String nameKey = AuthorList.list[widget.title]; - print('name $nameKey'); if (nameKey != null) { this._bodyList.add(authorTile(nameKey)); this._bodyList.add(Divider()); } - if (mdText.length > 30 || !this.model.config.state.isPro) { + if (mdText.length > 30) { this._bodyList.add(await MarkDownComp.Index(mdText)); // demo if (widget.demoChild != null && widget.demoChild.length > 0) { @@ -101,59 +97,36 @@ class IndexState extends State { @override Widget build(BuildContext context) { - return Store.connect(builder: (context, child, model) { - this.model = model; - return Scaffold( - appBar: AppBar( - //title: Text(this.title), - elevation: 0, - backgroundColor: Color(AppTheme.secondColor), - actions: this.getActions( - context, - ), - leading: IconButton( - icon: Icon(Icons.arrow_back), - //color: Theme.of(context).primaryTextTheme.title.color, - color: Color(AppTheme.blackColor), - onPressed: () => Navigator.pop(context), - ), + return Scaffold( + appBar: AppBar( + elevation: 0, + backgroundColor: Color(AppTheme.secondColor), + actions: this.getActions( + context, ), - body: this.loading ? this.renderLoading() : this.renderWidget(), - ); - }); + leading: IconButton( + icon: Icon(Icons.arrow_back), + color: Color(AppTheme.blackColor), + onPressed: () => Navigator.pop(context), + ), + ), + body: this.loading ? this.renderLoading() : this.renderWidget(), + ); } - openPage(context) async { - String url = widget.mdUrl; - // 加载页面 - if (this.model.config.state.isPro) { - FluroRouter.router.navigateTo(context, - '/webview?title=${widget.title}&url=${Uri.encodeComponent(this.model.config.state.env.githubAssetOrigin + url.replaceAll(RegExp('/index.md'), '').replaceAll('docs', 'lib'))}'); + Future getMdFile(url) async { + // bool productionEnv = Provide.value(context).isPro; + bool productionEnv = false; + if (productionEnv) { + return await FileUtils.readRemoteFile(url); } else { - // 加载本地 - String mdStr = await FileUtils.readLocaleFile(url); - Navigator.of(context).push( - MaterialPageRoute(builder: (BuildContext context) { - return BaseComp.Index( - title: widget.title, - child: (context, child, model) { - return MarkDownComp.Index(mdStr); - }, - ); - }), - ); + return await FileUtils.readLocaleFile(url); } } - Future getMdFile(url) async { - String mdStr = (await LoadAssetUtils.loadMarkdownAssets(url)).toString(); - return mdStr; - } - getActions(context) { return [ IconButton( - //color: Theme.of(context).primaryTextTheme.title.color, color: Color(AppTheme.blackColor), icon: Icon( Icons.insert_link, @@ -165,22 +138,13 @@ class IndexState extends State { ); }, ), - /* IconButton( - icon: Icon( - Icons.code, - ), - onPressed: () async { - this.openPage(context); - }, - ), */ IconButton( icon: Icon(Icons.share), - //color: Theme.of(context).primaryTextTheme.title.color, color: Color(AppTheme.blackColor), onPressed: () { - final String msg = - this.model.config.state.env.githubAssetOrigin + widget.mdUrl; - AppShare.shareText(msg); + dynamic origin = + Provide.value(context).env.githubAssetOrigin; + AppShare.shareText(origin + widget.mdUrl); }, ), ]; diff --git a/lib/controller/index.dart b/lib/controller/index.dart index 48c6efc..18bf0f6 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -1,5 +1,6 @@ -import 'package:efox_flutter/store/index.dart' show model; +// import 'package:efox_flutter/store/index.dart' show Store, ConfigModel, Provider; void initState() { - model.dispatch('config', 'setVersion'); + // Store.valueNotContext().setVersion(); +// Provider, } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 80bc417..c618f19 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,13 +3,13 @@ import 'package:flutter_localizations/flutter_localizations.dart'; //语言包 import 'package:efox_flutter/lang/index.dart' show AppLocalizationsDelegate, AppLocalizations; import 'package:efox_flutter/lang/config.dart' show ConfigLanguage; -import 'package:efox_flutter/store/index.dart' show model, Store; //引用Store 层 +import 'package:efox_flutter/store/index.dart' + show Store, ConfigModel; //引用Store 层 +import 'package:provide/provide.dart' show Provide; import 'package:efox_flutter/router/index.dart' show FluroRouter; //路由 import 'package:efox_flutter/config/theme.dart' show AppTheme; //主题 import 'package:efox_flutter/utils/analytics.dart' as Analytics; //统计 -void main() => runApp(MainApp()); - class MainApp extends StatefulWidget { MainApp() { FluroRouter.initRouter(); @@ -27,14 +27,16 @@ class MainAppState extends State { //实例化多语言 super.initState(); _delegate = AppLocalizationsDelegate(); - model.dispatch('config', 'getTheme'); + print('===main ============ $context'); } @override Widget build(BuildContext context) { - return Store.init( - model: model, - child: Store.connect(builder: (context, child, model) { + print('main view rebuild $context'); + Provide.value(context).getTheme(); + return Store.connect( + builder: (context, child, model) { + print('model===========================${model.theme}'); return MaterialApp( localeResolutionCallback: (deviceLocale, supportedLocales) { print( @@ -55,12 +57,13 @@ class MainAppState extends State { _delegate, ], supportedLocales: ConfigLanguage.supportedLocales, -// title: 'Flutter Demo', - theme: AppTheme.getThemeData(model.config.state.theme), + theme: AppTheme.getThemeData(model.theme), onGenerateRoute: FluroRouter.router.generator, navigatorObservers: [Analytics.observer], ); - }), + }, ); } } + +void main() => runApp(Store.init(child: MainApp())); diff --git a/lib/page/app-login/index.dart b/lib/page/app-login/index.dart index a799b44..5a62545 100644 --- a/lib/page/app-login/index.dart +++ b/lib/page/app-login/index.dart @@ -1,12 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/store/models/main_state_model.dart' - show MainStateModel; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; class Index extends StatefulWidget { - MainStateModel model; - Index({Key key, @required this.model}) : super(key: key); + Index({Key key}) : super(key: key); @override _IndexState createState() => _IndexState(); @@ -76,8 +73,8 @@ class _IndexState extends State { onPressed: () { if ((_formKey.currentState as FormState) .validate()) { - widget.model.dispatch('user', 'login', - {'name': nameCtl.text, 'pwd': pwdCtl.text}); + // widget.model.dispatch('user', 'login', + // {'name': nameCtl.text, 'pwd': pwdCtl.text}); } }, ), diff --git a/lib/page/component/tabs.dart b/lib/page/component/tabs.dart index 334075c..a64bf2e 100644 --- a/lib/page/component/tabs.dart +++ b/lib/page/component/tabs.dart @@ -1,14 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/store/models/main_state_model.dart' - show MainStateModel; import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'package:efox_flutter/widget/index.dart' as WidgetRoot; import 'package:efox_flutter/router/index.dart' show FluroRouter; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; class Index extends StatefulWidget { - final MainStateModel model; - Index({Key key, this.model}) : super(key: key); + Index({Key key}) : super(key: key); @override _IndexState createState() => new _IndexState(); } diff --git a/lib/page/home.dart b/lib/page/home.dart index 80e585d..2fce0c6 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -import 'package:efox_flutter/store/index.dart' show Store; import 'package:efox_flutter/controller/index.dart' as Controller; import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'component/tabs.dart' as TabIndex; @@ -17,13 +16,11 @@ class Index extends StatefulWidget { class _IndexState extends State { int _currentIndex = 0; PageController _pageController; - dynamic model; - @override void initState() { super.initState(); _pageController = PageController(); - Controller.initState(); + print('==============home context $context'); AppVersion().check(context); } @@ -33,7 +30,7 @@ class _IndexState extends State { super.dispose(); } - Widget _bottomNavigationBar(model) { + Widget _bottomNavigationBar() { return BottomNavigationBar( items: [ BottomNavigationBarItem( @@ -46,6 +43,8 @@ class _IndexState extends State { type: BottomNavigationBarType.fixed, currentIndex: _currentIndex, onTap: (int index) { + // TODO + // Controller.initState(); _pageController.jumpToPage(index); }, ); @@ -73,7 +72,7 @@ class _IndexState extends State { ), ), Text( - this.model.user.state.name ?? 'Guest', + 'Guest', style: TextStyle(fontWeight: FontWeight.bold), ) ], @@ -86,10 +85,9 @@ class _IndexState extends State { leading: Icon(Icons.account_circle), title: Text(AppLocalizations.$t('common.login')), onTap: () { - this.model.dispatch('user', 'setUser', {'name': 'Guest'}); Navigator.of(context).push( MaterialPageRoute(builder: (BuildContext context) { - return LoginIndex.Index(model: this.model,); + return LoginIndex.Index(); })); }, ), @@ -103,27 +101,22 @@ class _IndexState extends State { @override Widget build(BuildContext context) { - return Store.connect( - builder: (context, child, model) { - this.model = model; - return Scaffold( - drawer: renderDrawer(), - bottomNavigationBar: _bottomNavigationBar(model), - body: PageView( - controller: _pageController, - physics: NeverScrollableScrollPhysics(), - onPageChanged: (int index) { - setState(() { - _currentIndex = index; - }); - }, - children: [ - TabIndex.Index(model: model), - MyIndex.Index(model: model), - ], - ), - ); - }, + return Scaffold( + drawer: renderDrawer(), + bottomNavigationBar: _bottomNavigationBar(), + body: PageView( + controller: _pageController, + physics: NeverScrollableScrollPhysics(), + onPageChanged: (int index) { + setState(() { + _currentIndex = index; + }); + }, + children: [ + TabIndex.Index(), + MyIndex.Index(), + ], + ), ); } } diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index 1c53a1a..6dd501a 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'dart:io' show Platform; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -//import 'package:efox_flutter/router/index.dart' show FluroRouter; import 'package:efox_flutter/config/theme.dart' show AppTheme; -import 'package:efox_flutter/store/index.dart' show model; +import 'package:efox_flutter/store/index.dart' show ConfigModel, Provide; import 'package:efox_flutter/config/color.dart' show materialColor; import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; import 'package:efox_flutter/components/expansionTile.dart' as Comp; @@ -51,7 +50,7 @@ class _IndexState extends State { Widget build(BuildContext context) { List _EdageList = []; materialColor.forEach((k, v) { - _EdageList.add(this.Edage(k, v)); + _EdageList.add(this.Edage(k, v, context)); }); return Scaffold( appBar: AppBar( @@ -77,7 +76,9 @@ class _IndexState extends State { color: Color(AppTheme.lineColor), ), Comp.ExpansionTile( - leading: Icon(Icons.color_lens), + leading: Icon( + Icons.color_lens, + ), headerBackgroundColor: Colors.transparent, title: Row( children: [ @@ -85,7 +86,8 @@ class _IndexState extends State { Container( margin: EdgeInsets.fromLTRB(5, 5, 0, 0), child: Container( - color: Color(materialColor[model.config.state.theme]), + color: Color(materialColor[ + Provide.value(context).theme]), height: 15, width: 15, ), @@ -117,7 +119,7 @@ class _IndexState extends State { child: Row( mainAxisSize: MainAxisSize.min, children: [ - Text(model.config.state.appVersion), + Text(Provide.value(context).appVersion), Icon(Icons.navigate_next) ], ), @@ -132,10 +134,10 @@ class _IndexState extends State { )); } - Widget Edage(name, color) { + Widget Edage(name, color, context) { return GestureDetector( onTap: () { - model.dispatch('config', 'setTheme', name); + Provide.value(context).setTheme(name); }, child: Container( color: Color(color), diff --git a/lib/router/index.dart b/lib/router/index.dart index 27a8afe..1841321 100644 --- a/lib/router/index.dart +++ b/lib/router/index.dart @@ -10,6 +10,10 @@ import 'package:efox_flutter/utils/analytics.dart' show analytics; class FluroRouter { static Router router; + static webview({context, title, url}) { + router.navigateTo(context, '/webview?title=${title}&url=${url}'); + } + static Router initRouter() { FluroRouter.router = Router(); router.define( diff --git a/lib/store/http.dart b/lib/store/http.dart index cc40737..8ab5f9e 100644 --- a/lib/store/http.dart +++ b/lib/store/http.dart @@ -1,13 +1,44 @@ -import 'package:dio/dio.dart' show Dio, Options, DioError, BaseOptions; +import 'package:dio/dio.dart' show Dio, DioError, Options, InterceptorsWrapper, RequestOptions, Response; Dio getDio([Options options]) { - Dio dio = new Dio( - BaseOptions( - connectTimeout: 30 * 1000, - receiveTimeout: 30 * 1000, - headers: options.headers, - ), - ); // with default Options + if (options == null) { + options = Options( + headers: { + 'context-type': 'application/json', + 'user-agent': 'dio', + 'common-header': 'xx' + }, + ); + } + Dio dio = new Dio(); // with default Options + + // dio.options.baseUrl = "http://www.dtworkroom.com/doris/1/2.0.0/"; + dio.options.connectTimeout = 30 * 1000; //5s + dio.options.receiveTimeout = 30 * 1000; + dio.options.headers = options.headers; + + dio.interceptors.add(InterceptorsWrapper( + onRequest:(RequestOptions options){ + // Do something before request is sent + return options; //continue + // If you want to resolve the request with some custom data, + // you can return a `Response` object or return `dio.resolve(data)`. + // If you want to reject the request with a error message, + // you can return a `DioError` object or return `dio.reject(errMsg)` + }, + onResponse:(Response response) { + // Do something with response data + print('onResponse --- $response'); + return response; // continue + }, + onError: (DioError e) { + // Do something with response error + print('onError --- $e'); + return e;//continue + } +)); + + // Add request interceptor return dio; } @@ -21,9 +52,6 @@ Future get(url, [data = const {}]) async { } } -Future post(url, [data = const {}, params = const {}, Options options]) async { - return getDio(options).post( - url, - data: data - ); +Future post(url, [data = const {}, params = const {}, options]) async { + return getDio(options).post(url, data: data); } diff --git a/lib/store/index.dart b/lib/store/index.dart index 8598364..f3cae39 100644 --- a/lib/store/index.dart +++ b/lib/store/index.dart @@ -1,28 +1,48 @@ -import 'package:scoped_model/scoped_model.dart' show ScopedModel, ScopedModelDescendant; -import './models/main_state_model.dart' show MainStateModel; -//export './models/main_state_model.dart'; +import 'package:provide/provide.dart' + show Provider, Providers, ProviderNode, Provide; +// import 'package:flutter/material.dart' show StreamBuilder; +import './models/config_state_model.dart' show ConfigModel; +import './models/user_model.dart' show UserModel; +import './models/author_state_model.dart' show AuthorModel; +export './models/config_state_model.dart' show ConfigModel; +export './models/user_model.dart' show UserModel; +export './models/author_state_model.dart' show AuthorModel; +export 'package:provide/provide.dart' + show Provider, Providers, ProviderNode, Provide; -/** - * import 'package:efox_flutter/store/index.dart' as Store - * Store.model.config.state.isPro - */ class Store { - static init({model, child}) { - return ScopedModel(model: model, child: child); + static init({child}) { + final providers = Providers() + ..provide(Provider.value(ConfigModel())) + ..provide(Provider.value(UserModel())) + ..provide(Provider.value(AuthorModel())); + print('store init'); + return ProviderNode(providers: providers, child: child); } - - static get(context) => ScopedModel.of(context); - - static connect({ - builder, - child, - rebuildOnChange = true, - }) { - print('builder=$builder'); - return ScopedModelDescendant( - builder: builder, child: child, rebuildOnChange: rebuildOnChange); + static valueNotContext(value) { + return Providers().provideValue(value); + } + static value(context) { + return Provide.value(context); } -} -MainStateModel model = MainStateModel(); + static connect({builder}) { + return Provide(builder: builder); + } + // static connect({context, builder, child}) { + // final currentCounter = Provide.value(context); + // print('currentCounter ${currentCounter} '); + // return StreamBuilder( + // initialData: currentCounter, + // stream: Provide.stream(context).where((count) { + // return true; + // }), + // // .where((counter){ + // // 当返回true时,刷新页面 + // // return counter.value % 2 == 0; + // // }), + // builder: builder, + // ); + // } +} diff --git a/lib/store/models/author_state_model.dart b/lib/store/models/author_state_model.dart index 214d90b..ed4ca92 100644 --- a/lib/store/models/author_state_model.dart +++ b/lib/store/models/author_state_model.dart @@ -1,33 +1,25 @@ import '../objects/author_info.dart' show AuthorInfo; +import 'package:flutter/foundation.dart' show ChangeNotifier; -class AuthorModel { - get state => { - 'ken': AuthorInfo( - name: 'Ken', - avatarUrl: - 'https://avatars0.githubusercontent.com/u/3890513?s=400&v=4', - id: 'ken', - url: 'https://github.com/ckken', - ), - 'wanwu': AuthorInfo( - name: 'wanwu', - avatarUrl: - 'https://avatars3.githubusercontent.com/u/15372930?s=460&v=4', - id: 'wanwu', - url: 'https://github.com/wanwusangzhi', - ), - 'lhr': AuthorInfo( - name: 'Lin-Haoran', - avatarUrl: - 'https://avatars3.githubusercontent.com/u/30428314?s=400&v=4', - id: 'lhr', - url: 'https://github.com/DIVINER-only', - ), - }; - - methods(name, payload) async { - switch (name) { - case 'setAuthorInfo': break; - } - } +class AuthorModel extends ChangeNotifier { + Map list = { + 'ken': AuthorInfo( + name: 'Ken', + avatarUrl: 'https://avatars0.githubusercontent.com/u/3890513?s=400&v=4', + id: 'ken', + url: 'https://github.com/ckken', + ), + 'wanwu': AuthorInfo( + name: 'wanwu', + avatarUrl: 'https://avatars3.githubusercontent.com/u/15372930?s=460&v=4', + id: 'wanwu', + url: 'https://github.com/wanwusangzhi', + ), + 'lhr': AuthorInfo( + name: 'Lin-Haoran', + avatarUrl: 'https://avatars3.githubusercontent.com/u/30428314?s=400&v=4', + id: 'lhr', + url: 'https://github.com/DIVINER-only', + ), + }; } diff --git a/lib/store/models/config_state_model.dart b/lib/store/models/config_state_model.dart index 4c2b9c0..1637a99 100644 --- a/lib/store/models/config_state_model.dart +++ b/lib/store/models/config_state_model.dart @@ -1,9 +1,9 @@ import 'dart:convert'; import 'package:efox_flutter/config/index.dart' as Config; -import 'package:efox_flutter/store/index.dart' show model; -import 'package:efox_flutter/utils/loadAsset.dart' show loadAssets; +import 'package:efox_flutter/utils/loadAsset.dart' show readRemoteFile; import 'package:efox_flutter/utils/localstage.dart' show LocalStorage; import 'package:package_info/package_info.dart' show PackageInfo; +import 'package:flutter/foundation.dart' show ChangeNotifier; class ConfigInfo { bool isPro = Config.isPro; @@ -13,27 +13,30 @@ class ConfigInfo { String appVersion = '-'; } -ConfigInfo _appConfigInfo = new ConfigInfo(); - -class ConfigModel { - get state => _appConfigInfo; - +class ConfigModel extends ConfigInfo with ChangeNotifier { Future getAppVersion() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); - _appConfigInfo.appVersion = await packageInfo.version; + appVersion = await packageInfo.version; } Future getTheme() async { - String theme = await LocalStorage.get('theme'); - if (theme != null) { - _appConfigInfo.theme = theme; + String _theme = await LocalStorage.get('theme'); + print('config get Theme ${_theme}'); + if (_theme != null) { + setTheme(_theme); } } + Future setTheme(payload) async { + theme = payload; + LocalStorage.set('theme', payload); + notifyListeners(); + } + dynamic getVersion() async { - print('version ${model.config.state.env.versionUrl}'); + print('==================get version ===== ${this.env.versionUrl}'); String _version = - await loadAssets(model.config.state.env.versionUrl).then((resp) { + await readRemoteFile(this.env.versionUrl).then((resp) { Map res = json.decode(resp); return res['version'].toString() ?? '0.1'; }).catchError((err) { @@ -44,23 +47,8 @@ class ConfigModel { return _version; } - methods(name, payload) async { - print('payload= $payload'); - - switch (name) { - case 'setEnv': - _appConfigInfo.isPro = payload; - break; - case 'setVersion': - _appConfigInfo.version = await this.getVersion(); - break; - case 'setTheme': - _appConfigInfo.theme = payload; - LocalStorage.set('theme', payload); - break; - case 'getTheme': - await this.getTheme(); - break; - } + Future setVersion() async { + version = await this.getVersion(); + notifyListeners(); } } diff --git a/lib/store/models/main_state_model.dart b/lib/store/models/main_state_model.dart deleted file mode 100644 index 1b1a5d0..0000000 --- a/lib/store/models/main_state_model.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:scoped_model/scoped_model.dart'; -import 'user_model.dart' show UserModel; -import 'config_state_model.dart' show ConfigModel; -import 'author_state_model.dart' show AuthorModel; - -/** - * get state: model.modelName.state.xxx - * dispatch method: model.dispatch('modelName', 'methodsName', payload) - * - * import 'package:efox_flutter/store/index.dart' as Store - * Store.model.config.state.isPro - */ - -///主数据模型,需要全局使用的数据在这里添加模型 -class MainStateModel extends Model { - Map state = {}; - ConfigModel config = ConfigModel(); - AuthorModel author = AuthorModel(); - UserModel user = UserModel(); - - MainStateModel() { - // 初始化实例数据 - // order for dispatch to get destination model's methods - this.state = { - 'config': config, - 'author': author, - 'user': user - }; - //init model data - config.getAppVersion(); - // - } - - /** - * dispatch method: model.dispatch('modelName', 'methodsName', payload) - */ - Future dispatch(nameSpace, method, [payload]) async { - await this.state[nameSpace].methods(method, payload); - notifyListeners(); - return Future.value(this.state); - } -} - -//MainStateModel model = MainStateModel(); diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index 952170a..a88c373 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -1,17 +1,15 @@ import 'package:dio/dio.dart' show Options; import '../objects/user_info.dart' show UserInfo; import 'package:efox_flutter/store/http.dart' as Http; -import 'package:dio/dio.dart' show Dio, BaseOptions, Options; import 'dart:convert'; +import 'package:flutter/foundation.dart' show ChangeNotifier; Map Url = {'login': 'https://api.github.com/authorizations'}; -class UserModel extends Object { - UserInfo _userInfo = UserInfo(); - get state => _userInfo; - +class UserModel extends UserInfo with ChangeNotifier { setUser(payload) { - _userInfo = UserInfo(name: payload['name']); + name = payload['name']; + notifyListeners(); } Future login(payload) async { @@ -37,13 +35,4 @@ class UserModel extends Object { print(response); } - methods(name, payload) { - switch (name) { - case 'setUser': - setUser(payload); - break; - case 'login': - login(payload); - } - } } diff --git a/lib/utils/appVersion.dart b/lib/utils/appVersion.dart index 676a6c2..102b962 100644 --- a/lib/utils/appVersion.dart +++ b/lib/utils/appVersion.dart @@ -9,7 +9,6 @@ import 'package:efox_flutter/store/http.dart' as Http; import 'package:flutter/material.dart'; class AppVersion { - var _context; Future _checkPermission() async { PermissionStatus permission = await PermissionHandler() .checkPermissionStatus(PermissionGroup.storage); @@ -33,7 +32,6 @@ class AppVersion { } Future check(context, {showTips: false}) async { - _context = context; if (!Platform.isAndroid) return; // permission Status bool _permissisonReady = await this._checkPermission(); diff --git a/lib/utils/file.dart b/lib/utils/file.dart index f9591aa..d65fa59 100644 --- a/lib/utils/file.dart +++ b/lib/utils/file.dart @@ -1,6 +1,12 @@ import 'package:flutter/services.dart' show rootBundle; +import 'package:efox_flutter/store/http.dart' as Http; -Future readLocaleFile (path) async { - String content = await rootBundle.loadString('${path}', cache: false); - return content; -} \ No newline at end of file +Future readLocaleFile(path) async { + return await rootBundle.loadString('${path}', cache: false); +} + +Future readRemoteFile(path) async { + return await Http.get(path).then((res) { + return res; + }); +} diff --git a/lib/utils/loadAsset.dart b/lib/utils/loadAsset.dart index 510a9b0..d65fa59 100644 --- a/lib/utils/loadAsset.dart +++ b/lib/utils/loadAsset.dart @@ -1,21 +1,12 @@ -import 'package:efox_flutter/store/index.dart' show model; +import 'package:flutter/services.dart' show rootBundle; import 'package:efox_flutter/store/http.dart' as Http; -import 'file.dart' as FileUtil; -Future loadMarkdownAssets(path) async { - if (!model.config.state.isPro) { - return FileUtil.readLocaleFile(path); - } - String url = model.config.state.env.githubMarkdownOrigin + path + '?v=${model.config.state.version}'; - return Http.get(url).then((res) { - return res; - }); +Future readLocaleFile(path) async { + return await rootBundle.loadString('${path}', cache: false); } - -Future loadAssets(path) async { - return Http.get(path).then((res) { +Future readRemoteFile(path) async { + return await Http.get(path).then((res) { return res; }); } - diff --git a/lib/widget/gestures/gesturedetector/demo_scale.dart b/lib/widget/gestures/gesturedetector/demo_scale.dart index cebb264..27408f4 100644 --- a/lib/widget/gestures/gesturedetector/demo_scale.dart +++ b/lib/widget/gestures/gesturedetector/demo_scale.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:random_pk/random_pk.dart' show RandomContainer; class Index extends StatefulWidget { @override diff --git a/pubspec.yaml b/pubspec.yaml index 44564e6..8fa2882 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,7 +25,7 @@ dependencies: share: ^0.6.0+1 #社会化分享 pull_to_refresh: ^1.1.6 #加载更多 shared_preferences: ^0.4.2 #简单数据存储 - scoped_model: ^1.0.1 + provide: ^1.0.2 #数据管理层 dio: ^2.0.14 cached_network_image: ^0.5.1 intl: ^0.15.7 From b81b35c6c6a83c76426039444b12a6b593b6f743 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Tue, 26 Mar 2019 22:14:21 +0800 Subject: [PATCH 047/100] =?UTF-8?q?refactor:provide=E5=B1=82=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/store/provide_model/config_model.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/store/provide_model/config_model.dart b/lib/store/provide_model/config_model.dart index 0679617..452cd1a 100644 --- a/lib/store/provide_model/config_model.dart +++ b/lib/store/provide_model/config_model.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:efox_flutter/config/index.dart' as Config; -import 'package:efox_flutter/store/index.dart' show model; -import 'package:efox_flutter/utils/loadAsset.dart' show loadAssets; +import 'package:efox_flutter/utils/loadAsset.dart' show readRemoteFile; import 'package:efox_flutter/utils/localstage.dart' show LocalStorage; import 'package:package_info/package_info.dart' show PackageInfo; import 'package:flutter/material.dart' show ChangeNotifier; @@ -32,9 +31,9 @@ class ConfigModel extends ChangeNotifier { } dynamic getVersion() async { - print('version ${model.config.state.env.versionUrl}'); + print('version ${_appConfigInfo.env.versionUrl}'); String _version = - await loadAssets(model.config.state.env.versionUrl).then((resp) { + await readRemoteFile(_appConfigInfo.env.versionUrl).then((resp) { Map res = json.decode(resp); return res['version'].toString() ?? '0.1'; }).catchError((err) { From 6c06de190c1066094e7cb9694b546883d09f593c Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 26 Mar 2019 22:48:47 +0800 Subject: [PATCH 048/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E6=96=B9=E6=B3=95=20refact:=E5=90=88=E5=B9=B6model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/store/index.dart | 70 +++++++++++++++++---- lib/store/provide.dart | 76 ----------------------- lib/store/provide_model/config_model.dart | 66 -------------------- lib/store/provide_model/user_model.dart | 17 ----- 4 files changed, 59 insertions(+), 170 deletions(-) delete mode 100644 lib/store/provide.dart delete mode 100644 lib/store/provide_model/config_model.dart delete mode 100644 lib/store/provide_model/user_model.dart diff --git a/lib/store/index.dart b/lib/store/index.dart index f3cae39..595b873 100644 --- a/lib/store/index.dart +++ b/lib/store/index.dart @@ -1,35 +1,83 @@ +import 'package:flutter/material.dart' show StreamBuilder; import 'package:provide/provide.dart' - show Provider, Providers, ProviderNode, Provide; -// import 'package:flutter/material.dart' show StreamBuilder; + show + Provider, + Provide, + ProviderNode, + Providers, + ProvideMulti, + ProviderScope; +export 'package:provide/provide.dart'; import './models/config_state_model.dart' show ConfigModel; import './models/user_model.dart' show UserModel; import './models/author_state_model.dart' show AuthorModel; export './models/config_state_model.dart' show ConfigModel; export './models/user_model.dart' show UserModel; export './models/author_state_model.dart' show AuthorModel; -export 'package:provide/provide.dart' - show Provider, Providers, ProviderNode, Provide; class Store { - static init({child}) { + static init({model, child, dispose = true}) { final providers = Providers() ..provide(Provider.value(ConfigModel())) ..provide(Provider.value(UserModel())) ..provide(Provider.value(AuthorModel())); - print('store init'); - return ProviderNode(providers: providers, child: child); + + return ProviderNode( + child: child, + providers: providers, + dispose: dispose, + ); } + + /** + * 获取 + */ static valueNotContext(value) { return Providers().provideValue(value); } - static value(context) { - return Provide.value(context); + + /** + * 根据 Context 获取 + */ + static value(context, {scope}) { + return Provide.value(context, scope: scope); + } + + /** + * 监听 + */ + static connect({builder, child, scope}) { + return Provide( + builder: builder, + child: child, + scope: scope, + ); } - static connect({builder}) { - return Provide(builder: builder); + /** + * 通过流的方式 监听 + */ + static stream({builder, model, context}) { + return StreamBuilder( + initialData: model, + stream: Provide.stream(context), + builder: builder); } + /** + * 链接多个类型 + */ + static multi( + {builder, + child, + List requestedValues, + Map> requestedScopedValues}) { + return ProvideMulti( + builder: builder, + child: child, + requestedValues: requestedValues, + requestedScopedValues: requestedScopedValues); + } // static connect({context, builder, child}) { // final currentCounter = Provide.value(context); // print('currentCounter ${currentCounter} '); diff --git a/lib/store/provide.dart b/lib/store/provide.dart deleted file mode 100644 index ee12f70..0000000 --- a/lib/store/provide.dart +++ /dev/null @@ -1,76 +0,0 @@ -import './provide_model/user_model.dart' show UserModel; -import './provide_model/config_model.dart' show ConfigModel; -import 'package:provide/provide.dart' - show - Provider, - Provide, - ProviderNode, - Providers, - ProvideMulti, - ProviderScope; -import 'package:flutter/material.dart' show StreamBuilder; - -//导出类型 以便全局调用 -export './provide_model/user_model.dart'; -export './provide_model/config_model.dart'; - -/** - * import 'package:efox_flutter/store/provide.dart' as Store - * Store.userModel - * Store.configModel - */ -class Store { - static init({model, child, dispose = true}) { - Providers providers = Providers(); - UserModel userModel = UserModel(); - ConfigModel configModel = ConfigModel(); - providers - ..provide(Provider.value(userModel)) - ..provide(Provider.value(configModel)); - return ProviderNode( - child: child, - providers: providers, - dispose: dispose, - ); - } - - /** - * 获取 - */ - static get(context, {scope}) => Provide.value(context, scope: scope); - /** - * 监听 - */ - static connect({builder, child, scope}) { - return Provide( - builder: builder, - child: child, - scope: scope, - ); - } - - /** - * 通过流的方式 监听 - */ - static stream({builder, model, context}) { - return StreamBuilder( - initialData: model, - stream: Provide.stream(context), - builder: builder); - } - - /** - * 链接多个类型 - */ - static pm( - {builder, - child, - List requestedValues, - Map> requestedScopedValues}) { - return ProvideMulti( - builder: builder, - child: child, - requestedValues: requestedValues, - requestedScopedValues: requestedScopedValues); - } -} diff --git a/lib/store/provide_model/config_model.dart b/lib/store/provide_model/config_model.dart deleted file mode 100644 index 452cd1a..0000000 --- a/lib/store/provide_model/config_model.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'dart:convert'; -import 'package:efox_flutter/config/index.dart' as Config; -import 'package:efox_flutter/utils/loadAsset.dart' show readRemoteFile; -import 'package:efox_flutter/utils/localstage.dart' show LocalStorage; -import 'package:package_info/package_info.dart' show PackageInfo; -import 'package:flutter/material.dart' show ChangeNotifier; - -class ConfigInfo { - bool isPro = Config.isPro; - String version = '1.0'; - dynamic env = Config.env; - String theme = 'red'; - String appVersion = '-'; -} - -ConfigInfo _appConfigInfo = new ConfigInfo(); - -class ConfigModel extends ChangeNotifier { - get state => _appConfigInfo; - - Future getAppVersion() async { - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - _appConfigInfo.appVersion = await packageInfo.version; - } - - Future getTheme() async { - String theme = await LocalStorage.get('theme'); - if (theme != null) { - _appConfigInfo.theme = theme; - } - } - - dynamic getVersion() async { - print('version ${_appConfigInfo.env.versionUrl}'); - String _version = - await readRemoteFile(_appConfigInfo.env.versionUrl).then((resp) { - Map res = json.decode(resp); - return res['version'].toString() ?? '0.1'; - }).catchError((err) { - print('err $err'); - return '0.0'; - }); - print('_version ${_version}'); - return _version; - } - - methods(name, payload) async { - print('payload= $payload'); - - switch (name) { - case 'setEnv': - _appConfigInfo.isPro = payload; - break; - case 'setVersion': - _appConfigInfo.version = await this.getVersion(); - break; - case 'setTheme': - _appConfigInfo.theme = payload; - LocalStorage.set('theme', payload); - break; - case 'getTheme': - await this.getTheme(); - break; - } - } -} diff --git a/lib/store/provide_model/user_model.dart b/lib/store/provide_model/user_model.dart deleted file mode 100644 index b0391f5..0000000 --- a/lib/store/provide_model/user_model.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/material.dart'; -import '../objects/user_info.dart'; - -class UserModel extends ChangeNotifier { - UserInfo _userInfo = UserInfo(); - get userInfo => _userInfo; - - Future getAction() async { - _userInfo.loading = true; - notifyListeners(); - } - - void setAge() { - _userInfo.age += 1; - notifyListeners(); - } -} From bbd3becee041c9b2d06f8f3fd9d5748cbf166b5b Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Wed, 27 Mar 2019 22:14:41 +0800 Subject: [PATCH 049/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=AE=8C=E6=88=90=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/components/widgetComp.dart | 15 +- lib/controller/index.dart | 8 +- lib/lang/index.dart | 2 +- lib/main.dart | 7 +- lib/page/app-login/index.dart | 8 +- lib/page/home.dart | 34 ++- lib/page/mine/index.dart | 8 +- lib/store/http.dart | 85 ++++--- lib/store/index.dart | 29 +-- lib/store/models/config_state_model.dart | 31 +-- lib/store/models/user_model.dart | 71 ++++-- lib/store/objects/github_resp_info.dart | 94 ++++++++ lib/store/objects/user_info.dart | 223 ++++++++++++++---- lib/utils/appVersion.dart | 4 +- lib/utils/file.dart | 12 - lib/utils/loadAsset.dart | 4 +- .../{localstage.dart => localStorage.dart} | 0 17 files changed, 456 insertions(+), 179 deletions(-) create mode 100644 lib/store/objects/github_resp_info.dart delete mode 100644 lib/utils/file.dart rename lib/utils/{localstage.dart => localStorage.dart} (100%) diff --git a/lib/components/widgetComp.dart b/lib/components/widgetComp.dart index 95b5d67..dbdcf33 100644 --- a/lib/components/widgetComp.dart +++ b/lib/components/widgetComp.dart @@ -3,13 +3,13 @@ import 'package:efox_flutter/components/markdownComp.dart' as MarkDownComp; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/components/exampleComp.dart' as ExampleComp; import 'package:efox_flutter/components/updatingComp.dart' as UpdatingComp; -import 'package:efox_flutter/utils/file.dart' as FileUtils; +import 'package:efox_flutter/utils/loadAsset.dart' as AssetUtils; import 'package:efox_flutter/router/index.dart' show FluroRouter; import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'package:efox_flutter/utils/share.dart' as AppShare; import 'package:efox_flutter/widget/author_list.dart' as AuthorList; import 'package:efox_flutter/store/objects/author_info.dart' show AuthorInfo; -import 'package:efox_flutter/store/index.dart' show AuthorModel, ConfigModel, Provide; +import 'package:efox_flutter/store/index.dart' show AuthorModel, ConfigModel, Store; class Index extends StatefulWidget { final List demoChild; @@ -40,7 +40,7 @@ class IndexState extends State { } authorTile(nameKey) { - AuthorInfo info = Provide.value(context).list[nameKey]; + AuthorInfo info = Store.value(context).list[nameKey]; return Container( child: ListTile( onTap: () { @@ -97,6 +97,7 @@ class IndexState extends State { @override Widget build(BuildContext context) { + print('widgetcomp context =$context'); return Scaffold( appBar: AppBar( elevation: 0, @@ -115,12 +116,12 @@ class IndexState extends State { } Future getMdFile(url) async { - // bool productionEnv = Provide.value(context).isPro; + // bool productionEnv = Store.value(context).isPro; bool productionEnv = false; if (productionEnv) { - return await FileUtils.readRemoteFile(url); + return await AssetUtils.readRemoteFile(url); } else { - return await FileUtils.readLocaleFile(url); + return await AssetUtils.readLocaleFile(url); } } @@ -143,7 +144,7 @@ class IndexState extends State { color: Color(AppTheme.blackColor), onPressed: () { dynamic origin = - Provide.value(context).env.githubAssetOrigin; + Store.value(context).env.githubAssetOrigin; AppShare.shareText(origin + widget.mdUrl); }, ), diff --git a/lib/controller/index.dart b/lib/controller/index.dart index 18bf0f6..94ab327 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -1,6 +1,8 @@ -// import 'package:efox_flutter/store/index.dart' show Store, ConfigModel, Provider; +import 'package:efox_flutter/store/index.dart' show Store, ConfigModel, UserModel; void initState() { - // Store.valueNotContext().setVersion(); -// Provider, + // 获取版本号 + Store.valueNotCtx().$getAppVersion(); + // 登录 + Store.valueNotCtx().$getUserInfo(); } \ No newline at end of file diff --git a/lib/lang/index.dart b/lib/lang/index.dart index a5f622b..95450cd 100644 --- a/lib/lang/index.dart +++ b/lib/lang/index.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:efox_flutter/lang/config.dart' as I18NConfig; import 'package:flutter/services.dart' show rootBundle; -import 'package:efox_flutter/utils/localstage.dart' show LocalStorage; +import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; class AppLocalizations { Locale _locale; // language diff --git a/lib/main.dart b/lib/main.dart index c618f19..c84cd9f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,6 @@ import 'package:efox_flutter/lang/index.dart' import 'package:efox_flutter/lang/config.dart' show ConfigLanguage; import 'package:efox_flutter/store/index.dart' show Store, ConfigModel; //引用Store 层 -import 'package:provide/provide.dart' show Provide; import 'package:efox_flutter/router/index.dart' show FluroRouter; //路由 import 'package:efox_flutter/config/theme.dart' show AppTheme; //主题 import 'package:efox_flutter/utils/analytics.dart' as Analytics; //统计 @@ -27,16 +26,16 @@ class MainAppState extends State { //实例化多语言 super.initState(); _delegate = AppLocalizationsDelegate(); - print('===main ============ $context'); } @override Widget build(BuildContext context) { print('main view rebuild $context'); - Provide.value(context).getTheme(); + Store.value(context).$getTheme(); + Store.setContext(context); return Store.connect( builder: (context, child, model) { - print('model===========================${model.theme}'); + print('store connect context =$context'); return MaterialApp( localeResolutionCallback: (deviceLocale, supportedLocales) { print( diff --git a/lib/page/app-login/index.dart b/lib/page/app-login/index.dart index 5a62545..f995e67 100644 --- a/lib/page/app-login/index.dart +++ b/lib/page/app-login/index.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; - +import 'package:efox_flutter/store/index.dart' show Store, UserModel; class Index extends StatefulWidget { Index({Key key}) : super(key: key); @@ -73,8 +73,10 @@ class _IndexState extends State { onPressed: () { if ((_formKey.currentState as FormState) .validate()) { - // widget.model.dispatch('user', 'login', - // {'name': nameCtl.text, 'pwd': pwdCtl.text}); + Store.value(context).$login({ + 'name': nameCtl.text, + 'pwd':pwdCtl.text + }); } }, ), diff --git a/lib/page/home.dart b/lib/page/home.dart index 2fce0c6..4632ffd 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -7,6 +7,7 @@ import 'mine/index.dart' as MyIndex; import 'app-login/index.dart' as LoginIndex; import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; +import 'package:efox_flutter/store/index.dart' show Store, UserModel; class Index extends StatefulWidget { @override @@ -21,6 +22,7 @@ class _IndexState extends State { super.initState(); _pageController = PageController(); print('==============home context $context'); + Controller.initState(); AppVersion().check(context); } @@ -43,14 +45,16 @@ class _IndexState extends State { type: BottomNavigationBarType.fixed, currentIndex: _currentIndex, onTap: (int index) { - // TODO - // Controller.initState(); _pageController.jumpToPage(index); }, ); } + /** + * 抽屉面板 + */ renderDrawer() { + print('renderDrawer $context'); return Drawer( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -71,10 +75,14 @@ class _IndexState extends State { ), ), ), - Text( - 'Guest', - style: TextStyle(fontWeight: FontWeight.bold), - ) + Store.connect( + builder: (context, child, model) { + return Text( + model.user.name ?? 'Guest', + style: TextStyle(fontWeight: FontWeight.bold), + ); + }, + ), ], ), ), @@ -91,6 +99,20 @@ class _IndexState extends State { })); }, ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text(AppLocalizations.$t('common.login')), + onTap: () { + Store.value(context).$getUserInfo(); + }, + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text(AppLocalizations.$t('common.login')), + onTap: () { + Store.value(context).$checkAuthentication(); + }, + ), ], ), ), diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index 6dd501a..32aa595 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'dart:io' show Platform; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/config/theme.dart' show AppTheme; -import 'package:efox_flutter/store/index.dart' show ConfigModel, Provide; +import 'package:efox_flutter/store/index.dart' show ConfigModel, Store; import 'package:efox_flutter/config/color.dart' show materialColor; import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; import 'package:efox_flutter/components/expansionTile.dart' as Comp; @@ -87,7 +87,7 @@ class _IndexState extends State { margin: EdgeInsets.fromLTRB(5, 5, 0, 0), child: Container( color: Color(materialColor[ - Provide.value(context).theme]), + Store.value(context).theme]), height: 15, width: 15, ), @@ -119,7 +119,7 @@ class _IndexState extends State { child: Row( mainAxisSize: MainAxisSize.min, children: [ - Text(Provide.value(context).appVersion), + Text(Store.value(context).appVersion), Icon(Icons.navigate_next) ], ), @@ -137,7 +137,7 @@ class _IndexState extends State { Widget Edage(name, color, context) { return GestureDetector( onTap: () { - Provide.value(context).setTheme(name); + Store.value(context).$setTheme(name); }, child: Container( color: Color(color), diff --git a/lib/store/http.dart b/lib/store/http.dart index 8ab5f9e..8b70893 100644 --- a/lib/store/http.dart +++ b/lib/store/http.dart @@ -1,12 +1,30 @@ -import 'package:dio/dio.dart' show Dio, DioError, Options, InterceptorsWrapper, RequestOptions, Response; +import 'package:dio/dio.dart' + show + Dio, + DioError, + Options, + InterceptorsWrapper, + RequestOptions, + // LogInterceptor, + Response; +import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; + +void _print(title, message) { + if (message == null) { + print('$title'); + } else { + print('=================$title============='); + print('$message'); + print('=================response end============='); + } + ; +} Dio getDio([Options options]) { if (options == null) { options = Options( headers: { 'context-type': 'application/json', - 'user-agent': 'dio', - 'common-header': 'xx' }, ); } @@ -16,42 +34,47 @@ Dio getDio([Options options]) { dio.options.connectTimeout = 30 * 1000; //5s dio.options.receiveTimeout = 30 * 1000; dio.options.headers = options.headers; - - dio.interceptors.add(InterceptorsWrapper( - onRequest:(RequestOptions options){ - // Do something before request is sent - return options; //continue - // If you want to resolve the request with some custom data, - // you can return a `Response` object or return `dio.resolve(data)`. - // If you want to reject the request with a error message, - // you can return a `DioError` object or return `dio.reject(errMsg)` - }, - onResponse:(Response response) { - // Do something with response data - print('onResponse --- $response'); - return response; // continue - }, - onError: (DioError e) { - // Do something with response error - print('onError --- $e'); - return e;//continue - } -)); // Add request interceptor + dio.interceptors + .add(InterceptorsWrapper(onRequest: (RequestOptions options) async { + String token = await LocalStorage.get('githubRespLoginToken') ?? ''; + if (options.headers['Authorization'] == null) { + options.headers['Authorization'] = 'token $token'; + } + // Do something before request is sent + return options; //continue + // If you want to resolve the request with some custom data, + // you can return a `Response` object or return `dio.resolve(data)`. + // If you want to reject the request with a error message, + // you can return a `DioError` object or return `dio.reject(errMsg)` + }, onResponse: (Response response) { + // Do something with response data + _print('http.dart response success', response); + return response; // continue + }, onError: (DioError e) { + // Do something with response error + _print('http.dart response fail', + '${e.response.data} status: ${e.response.statusCode}'); + // return e; //continue + })); + // dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志 + return dio; } -Future get(url, [data = const {}]) async { +Future get({url, data = const {}}) async { try { - return await getDio().get(url).then((res) { - return res.data; - }); + return getDio().get(url).then((resp) => resp.data); } on DioError catch (e) { - return e.response; + return {'code': e.response.statusCode, 'msg': e.response.data}; } } -Future post(url, [data = const {}, params = const {}, options]) async { - return getDio(options).post(url, data: data); +Future post({url, data = const {}, options}) async { + try { + return getDio(options).post(url, data: data).then((resp) => resp.data); + } on DioError catch (e) { + return {'code': e.response.statusCode, 'msg': e.response.data}; + } } diff --git a/lib/store/index.dart b/lib/store/index.dart index 595b873..5d6cba7 100644 --- a/lib/store/index.dart +++ b/lib/store/index.dart @@ -16,6 +16,7 @@ export './models/user_model.dart' show UserModel; export './models/author_state_model.dart' show AuthorModel; class Store { + static dynamic ctx; static init({model, child, dispose = true}) { final providers = Providers() ..provide(Provider.value(ConfigModel())) @@ -29,17 +30,24 @@ class Store { ); } + static setContext(context) { + print('setContext $ctx'); + ctx = context; + print('context $ctx'); + } + /** * 获取 */ - static valueNotContext(value) { - return Providers().provideValue(value); + static T valueNotCtx() { + print('============valueNotCtx $ctx '); + return Provide.value(ctx); } /** * 根据 Context 获取 */ - static value(context, {scope}) { + static T value(context, {scope}) { return Provide.value(context, scope: scope); } @@ -78,19 +86,4 @@ class Store { requestedValues: requestedValues, requestedScopedValues: requestedScopedValues); } - // static connect({context, builder, child}) { - // final currentCounter = Provide.value(context); - // print('currentCounter ${currentCounter} '); - // return StreamBuilder( - // initialData: currentCounter, - // stream: Provide.stream(context).where((count) { - // return true; - // }), - // // .where((counter){ - // // 当返回true时,刷新页面 - // // return counter.value % 2 == 0; - // // }), - // builder: builder, - // ); - // } } diff --git a/lib/store/models/config_state_model.dart b/lib/store/models/config_state_model.dart index 1637a99..6f9913e 100644 --- a/lib/store/models/config_state_model.dart +++ b/lib/store/models/config_state_model.dart @@ -1,7 +1,5 @@ -import 'dart:convert'; import 'package:efox_flutter/config/index.dart' as Config; -import 'package:efox_flutter/utils/loadAsset.dart' show readRemoteFile; -import 'package:efox_flutter/utils/localstage.dart' show LocalStorage; +import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; import 'package:package_info/package_info.dart' show PackageInfo; import 'package:flutter/foundation.dart' show ChangeNotifier; @@ -14,41 +12,24 @@ class ConfigInfo { } class ConfigModel extends ConfigInfo with ChangeNotifier { - Future getAppVersion() async { + Future $getAppVersion() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); appVersion = await packageInfo.version; + notifyListeners(); } - Future getTheme() async { + Future $getTheme() async { String _theme = await LocalStorage.get('theme'); print('config get Theme ${_theme}'); if (_theme != null) { - setTheme(_theme); + $setTheme(_theme); } } - Future setTheme(payload) async { + Future $setTheme(payload) async { theme = payload; LocalStorage.set('theme', payload); notifyListeners(); } - dynamic getVersion() async { - print('==================get version ===== ${this.env.versionUrl}'); - String _version = - await readRemoteFile(this.env.versionUrl).then((resp) { - Map res = json.decode(resp); - return res['version'].toString() ?? '0.1'; - }).catchError((err) { - print('err $err'); - return '0.0'; - }); - print('_version ${_version}'); - return _version; - } - - Future setVersion() async { - version = await this.getVersion(); - notifyListeners(); - } } diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index a88c373..d12a967 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -1,38 +1,67 @@ import 'package:dio/dio.dart' show Options; import '../objects/user_info.dart' show UserInfo; +import '../objects/github_resp_info.dart' show GitHubRespInfo; import 'package:efox_flutter/store/http.dart' as Http; import 'dart:convert'; import 'package:flutter/foundation.dart' show ChangeNotifier; +import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; -Map Url = {'login': 'https://api.github.com/authorizations'}; - -class UserModel extends UserInfo with ChangeNotifier { - setUser(payload) { - name = payload['name']; +class UserModel with ChangeNotifier { + UserInfo user = UserInfo(); + $setUserInfo(payload) { + user = payload; notifyListeners(); } - Future login(payload) async { + Future $login(payload) async { var name = payload['name']; var pwd = payload['pwd']; var bytes = utf8.encode("$name:$pwd"); var credentials = base64.encode(bytes); + const data = { + "scopes": ["user", "repo", "gist", "notifications", "public_repo"], + "note": "admin_script", + "client_id": "d8eef6133f1a2be3a842", + "client_secret": "2b005eed01c72aefd68fac5c5c7f2654f81c227a" + }; + Options options = Options(headers: {'Authorization': 'Basic $credentials'}); + var response = await Http.post( + url: 'https://api.github.com/authorizations', + data: data, + options: options, + ); + + GitHubRespInfo user = GitHubRespInfo.fromJson(response); + print('GitHubRespInfo $user'); + await LocalStorage.set('githubRespInfo', response.toString()); + await LocalStorage.set('githubRespLoginToken', user.token.toString()); + await $getUserInfo(); + } + + /** + * 获取用户信息 + */ + Future $getUserInfo() async { var response = await Http.post( - Url['login'], - { - "scopes": ["user", "repo", "gist", "notifications", "public_repo"], - "note": "admin_script", - }, - {}, - Options(headers: {'Authorization': 'Basic $credentials'})).then((res) { - print('success ==$res'); - }).catchError((err) { - print('error -------------- ${err.response}'); - print('error -------------- ${err.request.data}'); - print('error -------------- ${err.request.headers}'); - }); - print('response =---------------------'); - print(response); + url: 'https://api.github.com/user', + ); + UserInfo user = UserInfo.fromJson(response); + $setUserInfo(user); } + /** + * 校验登录态 + */ + Future $checkAuthentication() async { + String token = await LocalStorage.get('githubRespLoginToken'); + if (token == null) { + return false; + } + ; + const client_id = 'd8eef6133f1a2be3a842'; + var response = await Http.get( + url: 'https://api.github.com/applications/$client_id/tokens/$token'); + print('response ${response} ${response['code']} ${response['msg']}'); + return true; + } } diff --git a/lib/store/objects/github_resp_info.dart b/lib/store/objects/github_resp_info.dart new file mode 100644 index 0000000..01e60c2 --- /dev/null +++ b/lib/store/objects/github_resp_info.dart @@ -0,0 +1,94 @@ +/** + * github登录后返回的数据 + */ +class GitHubRespInfo extends Object { + num id; + String url; + Map app; + String token; + String hashed_token; + String token_last_eight; + String note; + Map note_url; + String created_at; + String updated_at; + List scopes; + Map fingerprint; + + GitHubRespInfo({ + this.id, + this.url, + this.app, + this.token, + this.hashed_token, + this.token_last_eight, + this.note, + this.note_url, + this.created_at, + this.updated_at, + this.scopes, + this.fingerprint, + }); + + GitHubRespInfo.fromJson(json) { + id = json['id']; + url = json['url']; + app = json['app']; + token = json['token']; + hashed_token = json['hashed_token']; + token_last_eight = json['token_last_eight']; + note = json['note']; + note_url = json['note_url']; + created_at = json['created_at']; + updated_at = json['updated_at']; + scopes = json['scopes']; + fingerprint = json['fingerprint']; + } + + Map toJson(instance) => { + 'id': instance.id, + 'url': instance.url, + 'app': App.fromJson(instance.app), + 'token': instance.token, + 'hashed_token': instance.hashed_token, + 'token_last_eight': instance.token_last_eight, + 'note': instance.note, + 'note_url': instance.note_url, + 'created_at': instance.created_at, + 'updated_at': instance.updated_at, + 'scopes': instance.scopes, + 'fingerprint': instance.fingerprint, + }; +} + +class App extends Object { + String str; + num number; + bool boolean; + List array; + Map map; + + App({ + this.str, + this.number, + this.boolean, + this.array, + this.map, + }); + + App.fromJson(json) { + str = json['str']; + number = json['number']; + boolean = json['boolean']; + array = json['array']; + map = json['map']; + } + + Map toJson(instance) => { + 'str': instance.str, + 'number': instance.number, + 'boolean': instance.boolean, + 'array': instance.array, + 'map': instance.map, + }; +} diff --git a/lib/store/objects/user_info.dart b/lib/store/objects/user_info.dart index cf7403f..07dd61a 100644 --- a/lib/store/objects/user_info.dart +++ b/lib/store/objects/user_info.dart @@ -1,48 +1,191 @@ class UserInfo extends Object { - String fbid; - String uid; + String login; + num id; + String node_id; + String avatar_url; + String gravatar_id; + String url; + String html_url; + String followers_url; + String following_url; + String gists_url; + String starred_url; + String subscriptions_url; + String organizations_url; + String repos_url; + String events_url; + String received_events_url; + String type; + bool site_admin; String name; - String token; - String avatar; - int expiresIn; - int expireTime; - bool loading; - int age; + String company; + String blog; + dynamic location; + String email; + dynamic hireable; + dynamic bio; + num public_repos; + num public_gists; + num followers; + num following; + String created_at; + String updated_at; + num private_gists; + num total_private_repos; + num owned_private_repos; + num disk_usage; + num collaborators; + bool two_factor_authentication; + dynamic plan; + + UserInfo({ + this.login, + this.id, + this.node_id, + this.avatar_url, + this.gravatar_id, + this.url, + this.html_url, + this.followers_url, + this.following_url, + this.gists_url, + this.starred_url, + this.subscriptions_url, + this.organizations_url, + this.repos_url, + this.events_url, + this.received_events_url, + this.type, + this.site_admin, + this.name, + this.company, + this.blog, + this.location, + this.email, + this.hireable, + this.bio, + this.public_repos, + this.public_gists, + this.followers, + this.following, + this.created_at, + this.updated_at, + this.private_gists, + this.total_private_repos, + this.owned_private_repos, + this.disk_usage, + this.collaborators, + this.two_factor_authentication, + this.plan, + }); - // - UserInfo( - {this.fbid, - this.uid, - this.name, - this.token, - this.expiresIn, - this.age = 0, - this.loading = false, - this.expireTime = 0}); - // UserInfo.fromJson(json) { - Map user = json['user']; - fbid = user['fbid']; - uid = user['uid']; - name = user['name']; - avatar = user['avatar']; - token = json['token']; - expiresIn = json['expiresIn'] ?? 0; - if (expiresIn >= 0) { - int now = DateTime.now().millisecondsSinceEpoch; - expireTime = now + expiresIn * 1000; - } + login = json['login']; + id = json['id']; + node_id = json['node_id']; + avatar_url = json['avatar_url']; + gravatar_id = json['gravatar_id']; + url = json['url']; + html_url = json['html_url']; + followers_url = json['followers_url']; + following_url = json['following_url']; + gists_url = json['gists_url']; + starred_url = json['starred_url']; + subscriptions_url = json['subscriptions_url']; + organizations_url = json['organizations_url']; + repos_url = json['repos_url']; + events_url = json['events_url']; + received_events_url = json['received_events_url']; + type = json['type']; + site_admin = json['site_admin']; + name = json['name']; + company = json['company']; + blog = json['blog']; + location = json['location']; + email = json['email']; + hireable = json['hireable']; + bio = json['bio']; + public_repos = json['public_repos']; + public_gists = json['public_gists']; + followers = json['followers']; + following = json['following']; + created_at = json['created_at']; + updated_at = json['updated_at']; + private_gists = json['private_gists']; + total_private_repos = json['total_private_repos']; + owned_private_repos = json['owned_private_repos']; + disk_usage = json['disk_usage']; + collaborators = json['collaborators']; + two_factor_authentication = json['two_factor_authentication']; + plan = Plan.fromJson(json['plan']); + } + + Map toJson(instance) => { + 'login': instance.login, + 'id': instance.id, + 'node_id': instance.node_id, + 'avatar_url': instance.avatar_url, + 'gravatar_id': instance.gravatar_id, + 'url': instance.url, + 'html_url': instance.html_url, + 'followers_url': instance.followers_url, + 'following_url': instance.following_url, + 'gists_url': instance.gists_url, + 'starred_url': instance.starred_url, + 'subscriptions_url': instance.subscriptions_url, + 'organizations_url': instance.organizations_url, + 'repos_url': instance.repos_url, + 'events_url': instance.events_url, + 'received_events_url': instance.received_events_url, + 'type': instance.type, + 'site_admin': instance.site_admin, + 'name': instance.name, + 'company': instance.company, + 'blog': instance.blog, + 'location': instance.location, + 'email': instance.email, + 'hireable': instance.hireable, + 'bio': instance.bio, + 'public_repos': instance.public_repos, + 'public_gists': instance.public_gists, + 'followers': instance.followers, + 'following': instance.following, + 'created_at': instance.created_at, + 'updated_at': instance.updated_at, + 'private_gists': instance.private_gists, + 'total_private_repos': instance.total_private_repos, + 'owned_private_repos': instance.owned_private_repos, + 'disk_usage': instance.disk_usage, + 'collaborators': instance.collaborators, + 'two_factor_authentication': instance.two_factor_authentication, + 'plan': instance.plan, + }; +} + +class Plan extends Object { + String name; + num space; + num collaborators; + num private_repos; + + Plan({ + this.name, + this.space, + this.collaborators, + this.private_repos, + }); + + Plan.fromJson(Map json) { + name = json['name']; + space = json['space']; + collaborators = json['collaborators']; + private_repos = json['private_repos']; } - Map toJson(UserInfo instance) => { - 'user': { - 'fbid': instance.fbid, - 'uid': instance.uid, - 'name': instance.name, - 'avatar': instance.avatar, - }, - 'token': instance.token, - 'expiresIn': instance.expiresIn, - 'expireTime': instance.expireTime, + Map toJson(instance) => { + 'name': instance.name, + 'space': instance.space, + 'collaborators': instance.collaborators, + 'private_repos': instance.private_repos, }; } diff --git a/lib/utils/appVersion.dart b/lib/utils/appVersion.dart index 102b962..c44fe71 100644 --- a/lib/utils/appVersion.dart +++ b/lib/utils/appVersion.dart @@ -81,7 +81,9 @@ class AppVersion { Future checkVersion(String version, String platform) async { var res = await Http.get( - 'https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/version.json'); + url: + 'https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/version.json', + ); res = json.decode(res); print('res=${res['version']}'); String newVersion = (res['version'] != null) ? res['version'] : version; diff --git a/lib/utils/file.dart b/lib/utils/file.dart deleted file mode 100644 index d65fa59..0000000 --- a/lib/utils/file.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/services.dart' show rootBundle; -import 'package:efox_flutter/store/http.dart' as Http; - -Future readLocaleFile(path) async { - return await rootBundle.loadString('${path}', cache: false); -} - -Future readRemoteFile(path) async { - return await Http.get(path).then((res) { - return res; - }); -} diff --git a/lib/utils/loadAsset.dart b/lib/utils/loadAsset.dart index d65fa59..1ad6ee1 100644 --- a/lib/utils/loadAsset.dart +++ b/lib/utils/loadAsset.dart @@ -6,7 +6,5 @@ Future readLocaleFile(path) async { } Future readRemoteFile(path) async { - return await Http.get(path).then((res) { - return res; - }); + return await Http.get(url: path); } diff --git a/lib/utils/localstage.dart b/lib/utils/localStorage.dart similarity index 100% rename from lib/utils/localstage.dart rename to lib/utils/localStorage.dart From 22ced2930637f58904f17e51ddb93b365ac4aba0 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Thu, 28 Mar 2019 21:32:23 +0800 Subject: [PATCH 050/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0loading=E2=80=98?= =?UTF-8?q?=20=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/index.dart | 82 ++++++++++++++++++++ lib/http/loading.dart | 43 +++++++++++ lib/http/loadingDialog.dart | 64 ++++++++++++++++ lib/http/log.dart | 12 +++ lib/main.dart | 7 +- lib/page/app-login/index.dart | 126 ++++++++++++++++--------------- lib/page/component/tabs.dart | 12 ++- lib/page/home.dart | 52 ++++++------- lib/page/mine/index.dart | 9 ++- lib/store/http.dart | 80 -------------------- lib/store/index.dart | 27 +++++-- lib/store/models/user_model.dart | 78 +++++++++++++------ lib/utils/appVersion.dart | 29 +++---- lib/utils/loadAsset.dart | 2 +- 14 files changed, 397 insertions(+), 226 deletions(-) create mode 100644 lib/http/index.dart create mode 100644 lib/http/loading.dart create mode 100644 lib/http/loadingDialog.dart create mode 100644 lib/http/log.dart delete mode 100644 lib/store/http.dart diff --git a/lib/http/index.dart b/lib/http/index.dart new file mode 100644 index 0000000..7eb988f --- /dev/null +++ b/lib/http/index.dart @@ -0,0 +1,82 @@ +import 'package:dio/dio.dart' + show + Dio, + DioError, + Options, + InterceptorsWrapper, + RequestOptions, + LogInterceptor, + Response; +import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; +import 'log.dart' show log; +import 'package:efox_flutter/store/index.dart' show Store; +import 'package:flutter/material.dart'; +import 'loadingDialog.dart' show showAppLoading; + +Dio getDio([Options options]) { + if (options == null) { + options = Options( + headers: { + 'context-type': 'application/json', + }, + ); + } + Dio dio = new Dio(); // with default Options + + // dio.options.baseUrl = "http://www.dtworkroom.com/doris/1/2.0.0/"; + dio.options.connectTimeout = 30 * 1000; //5s + dio.options.receiveTimeout = 30 * 1000; + dio.options.headers = options.headers; + + // Add request interceptor + dio.interceptors.add(InterceptorsWrapper( + onRequest: (RequestOptions options) async { + String token = await LocalStorage.get('githubRespLoginToken'); + if (options.headers['Authorization'] == null && token != null) { + options.headers['Authorization'] = 'token $token'; + } + print( + ' Store.widgetCtx -------------------------------- ${Store.widgetCtx}'); + showAppLoading(); + log('【发送请求】 ${options.uri} ', '${options.headers} ${options.data}'); + // Do something before request is sent + return options; //continue + // If you want to resolve the request with some custom data, + // you can return a `Response` object or return `dio.resolve(data)`. + // If you want to reject the request with a error message, + // you can return a `DioError` object or return `dio.reject(errMsg)` + }, + onResponse: (Response response) async { + log('【请求成功】 ${response.request.uri},【状态码 ${response.statusCode}】', + response); + showAppLoading(); + return {'data': response.data}; // continue + }, + onError: (DioError e) async { + // showAppLoading(); + dynamic msg = e.message; + dynamic code = 0; // 错误码 + dynamic status = e.type; // http请求状态 + if (e.response != null && e.response.data != null) { + code = 1; + msg = e.response.data; + status = e.response.statusCode; + } + log('【请求失败】 ${e.request.uri},【状态码 ${status}】【code】: ${code}', msg); + return {'msg': msg, 'code': code, 'status': status}; + }, + )); + dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志 + + return dio; +} + +Future get({url, data = const {}}) async { + return getDio().get(url).then((resp) => resp.data); +} + +Future post({url, data = const {}, options}) async { + return getDio(options).post(url, data: data).then((resp) { + return resp.data; + }); +} diff --git a/lib/http/loading.dart b/lib/http/loading.dart new file mode 100644 index 0000000..a6874e0 --- /dev/null +++ b/lib/http/loading.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +// ignore: must_be_immutable +class NetLoadingDialog extends StatefulWidget { + String loadingText; + bool outsideDismiss; + + Function dismissDialog; + + NetLoadingDialog( + {Key key, + this.loadingText = "loading...", + this.outsideDismiss = true, + this.dismissDialog}) + : super(key: key); + + @override + State createState() => _LoadingDialog(); +} + +class _LoadingDialog extends State { + _dismissDialog() { + Navigator.of(context).pop(); + } + + @override + void initState() { + super.initState(); + if (widget.dismissDialog != null) { + widget.dismissDialog( + + //将关闭 dialog的方法传递到调用的页面. + (){Navigator.of(context).pop();} + + ); + } + } + + @override + Widget build(BuildContext context) { + return Text('haha'); + } +} \ No newline at end of file diff --git a/lib/http/loadingDialog.dart b/lib/http/loadingDialog.dart new file mode 100644 index 0000000..063a035 --- /dev/null +++ b/lib/http/loadingDialog.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:efox_flutter/store/index.dart' show Store; + +OverlayEntry overlayEntry = null; +void showAppLoading() { + // showDialog( + // context: Store.widgetCtx, + // builder: (context) { + // return Center( + // child: CircularProgressIndicator( + // // value: 0.5, + // semanticsLabel: 'hsh', + // semanticsValue: 'sds', + // ), + // ); + // }); + if (overlayEntry?.maintainState == true) { + print('移除啦~~~'); + overlayEntry.maintainState = false; + overlayEntry.remove(); + return; + } + var overlayState = Overlay.of(Store.widgetCtx); + overlayEntry = new OverlayEntry(builder: (context) { + return Scaffold( + backgroundColor: Colors.transparent, + body: GestureDetector( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [CircularProgressIndicator(), Text('loading...')], + ), + onTap: () { + print('移除啦~~~ ${overlayEntry.maintainState}'); + overlayEntry.maintainState = false; + overlayEntry?.remove(); + print('overlayEntry ${overlayEntry}'); + }, + ), + ); + }); + // 加载框 + overlayState.insert(overlayEntry); + overlayEntry.maintainState = true; + + // Navigator.of(Store.widgetCtx).push(MaterialPageRoute(builder: (context) { + // return Index(); + // })); +} + +// class Index extends Dialog { +// @override +// Widget build(BuildContext context) { +// final ThemeData theme = Theme.of(context, shadowThemeOnly: true); +// return Scaffold( +// backgroundColor: Colors.transparent, +// body: Center( +// child: Column( +// children: [CircularProgressIndicator(), Text('loading...')], +// ), +// ), +// ); +// } +// } diff --git a/lib/http/log.dart b/lib/http/log.dart new file mode 100644 index 0000000..b695293 --- /dev/null +++ b/lib/http/log.dart @@ -0,0 +1,12 @@ +/** + * 日志打印 + */ +void log(title, message) { + if (message == null) { + print('$title'); + } else { + print('$title'); + print('$message'); + print('end'); + }; +} diff --git a/lib/main.dart b/lib/main.dart index c84cd9f..3364c80 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -26,16 +26,15 @@ class MainAppState extends State { //实例化多语言 super.initState(); _delegate = AppLocalizationsDelegate(); + Store.setStoreCtx(context); // 初始化数据层 } @override Widget build(BuildContext context) { - print('main view rebuild $context'); Store.value(context).$getTheme(); - Store.setContext(context); + return Store.connect( builder: (context, child, model) { - print('store connect context =$context'); return MaterialApp( localeResolutionCallback: (deviceLocale, supportedLocales) { print( @@ -45,7 +44,7 @@ class MainAppState extends State { : Locale('zh'); return _locale; }, - onGenerateTitle: (context) { + onGenerateTitle: (ctx) { // 设置多语言代理 AppLocalizations.setProxy(setState, _delegate); return 'flutter'; diff --git a/lib/page/app-login/index.dart b/lib/page/app-login/index.dart index f995e67..de1a1a7 100644 --- a/lib/page/app-login/index.dart +++ b/lib/page/app-login/index.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/store/index.dart' show Store, UserModel; -class Index extends StatefulWidget { +class Index extends StatefulWidget { Index({Key key}) : super(key: key); @override @@ -15,7 +15,7 @@ class _IndexState extends State { GlobalKey _formKey = GlobalKey(); @override - Widget build(BuildContext context) { + Widget build(BuildContext ctx) { return Scaffold( appBar: AppBar( centerTitle: true, @@ -25,70 +25,74 @@ class _IndexState extends State { ), automaticallyImplyLeading: false, ), - body: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 50, horizontal: 24), - child: Form( - key: _formKey, - autovalidate: true, - child: Column( - children: [ - TextFormField( - controller: nameCtl, - autofocus: true, - decoration: InputDecoration( - labelText: '账户名', - hintText: '请输入Github账户名', - icon: Icon(Icons.person), + body: Builder(builder: (BuildContext context) { + // Store.setWidgetCtx(context); + return SingleChildScrollView( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 50, horizontal: 24), + child: Form( + key: _formKey, + autovalidate: true, + child: Column( + children: [ + TextFormField( + controller: nameCtl, + autofocus: true, + decoration: InputDecoration( + labelText: '账户名', + hintText: '请输入Github账户名', + icon: Icon(Icons.person), + ), + validator: (v) { + return v.trim().length > 0 ? null : '用户名不能为空'; + }, ), - validator: (v) { - return v.trim().length > 0 ? null : '用户名不能为空'; - }, - ), - TextFormField( - controller: pwdCtl, - decoration: InputDecoration( - labelText: '密码', - hintText: '请输入登录密码', - icon: Icon(Icons.lock), + TextFormField( + controller: pwdCtl, + decoration: InputDecoration( + labelText: '密码', + hintText: '请输入登录密码', + icon: Icon(Icons.lock), + ), + obscureText: true, + validator: (v) { + return v.trim().length > 5 ? null : "密码不能少于6位"; + }, ), - obscureText: true, - validator: (v) { - return v.trim().length > 5 ? null : "密码不能少于6位"; - }, - ), - Padding( - padding: EdgeInsets.only(top: 50), - child: Row( - children: [ - Expanded( - child: RaisedButton( - padding: EdgeInsets.all(15), - color: Theme.of(context).primaryColor, - textColor: - Theme.of(context).primaryTextTheme.title.color, - child: Text( - AppLocalizations.$t('common.login'), + Padding( + padding: EdgeInsets.only(top: 50), + child: Row( + children: [ + Expanded( + child: RaisedButton( + padding: EdgeInsets.all(15), + color: Theme.of(context).primaryColor, + textColor: + Theme.of(context).primaryTextTheme.title.color, + child: Text( + AppLocalizations.$t('common.login'), + ), + onPressed: () async { + if ((_formKey.currentState as FormState) + .validate()) { + Store.value(context) + .$loginController(context, { + 'name': nameCtl.text, + 'pwd': pwdCtl.text + }); + } + }, ), - onPressed: () { - if ((_formKey.currentState as FormState) - .validate()) { - Store.value(context).$login({ - 'name': nameCtl.text, - 'pwd':pwdCtl.text - }); - } - }, - ), - ) - ], - ), - ) - ], + ) + ], + ), + ) + ], + ), ), ), - ), - ), + ); + }), ); } } diff --git a/lib/page/component/tabs.dart b/lib/page/component/tabs.dart index a64bf2e..a41d086 100644 --- a/lib/page/component/tabs.dart +++ b/lib/page/component/tabs.dart @@ -123,10 +123,14 @@ class _IndexState extends State ], ), onPressed: () { - FluroRouter.router.navigateTo( - context, - nameSpaces + _tmpWidgetList[index].title, - ); + // FluroRouter.router.navigateTo( + // context, + // nameSpaces + _tmpWidgetList[index].title, + // ); + + Scaffold.of(context).showSnackBar(SnackBar( + content: Text('haha'), + )); }, ), ); diff --git a/lib/page/home.dart b/lib/page/home.dart index 4632ffd..5998b1a 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -22,6 +22,7 @@ class _IndexState extends State { super.initState(); _pageController = PageController(); print('==============home context $context'); + // Store.setContext(context); Controller.initState(); AppVersion().check(context); } @@ -64,26 +65,26 @@ class _IndexState extends State { decoration: BoxDecoration( color: Color(AppTheme.mainColor), ), - child: Row( - children: [ - Padding( - padding: EdgeInsets.symmetric(horizontal: 20.0), - child: ClipOval( - child: Image.asset( - "assets/imgs/avatar.png", - width: 80, + child: Store.connect( + builder: (context, child, model) { + return Row(children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 20.0), + child: ClipOval( + child: model.user.avatar_url != null + ? Image.network( + model.user.avatar_url, + width: 80, + ) + : Icon(Icons.account_box), ), ), - ), - Store.connect( - builder: (context, child, model) { - return Text( - model.user.name ?? 'Guest', - style: TextStyle(fontWeight: FontWeight.bold), - ); - }, - ), - ], + Text( + Store.value(context).user.name ?? 'Guest', + style: TextStyle(fontWeight: FontWeight.bold), + ) + ]); + }, ), ), Expanded( @@ -99,18 +100,12 @@ class _IndexState extends State { })); }, ), + Divider(), ListTile( - leading: Icon(Icons.account_circle), - title: Text(AppLocalizations.$t('common.login')), - onTap: () { - Store.value(context).$getUserInfo(); - }, - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text(AppLocalizations.$t('common.login')), + leading: Icon(Icons.exit_to_app), + title: Text(AppLocalizations.$t('common.logout')), onTap: () { - Store.value(context).$checkAuthentication(); + Store.value(context).$clearUserInfo(); }, ), ], @@ -123,6 +118,7 @@ class _IndexState extends State { @override Widget build(BuildContext context) { + Store.setWidgetCtx(context); // 初始化scaffold的上下文作为全局上下文,提供弹窗等使用。 return Scaffold( drawer: renderDrawer(), bottomNavigationBar: _bottomNavigationBar(), diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index 32aa595..e129aa7 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -54,9 +54,12 @@ class _IndexState extends State { }); return Scaffold( appBar: AppBar( - elevation: 0, - centerTitle: true, - title: Text(AppLocalizations.$t('title_my'))), + elevation: 0, + centerTitle: true, + title: Text( + AppLocalizations.$t('title_my'), + ), + ), body: ListView( children: [ ListTile( diff --git a/lib/store/http.dart b/lib/store/http.dart deleted file mode 100644 index 8b70893..0000000 --- a/lib/store/http.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:dio/dio.dart' - show - Dio, - DioError, - Options, - InterceptorsWrapper, - RequestOptions, - // LogInterceptor, - Response; -import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; - -void _print(title, message) { - if (message == null) { - print('$title'); - } else { - print('=================$title============='); - print('$message'); - print('=================response end============='); - } - ; -} - -Dio getDio([Options options]) { - if (options == null) { - options = Options( - headers: { - 'context-type': 'application/json', - }, - ); - } - Dio dio = new Dio(); // with default Options - - // dio.options.baseUrl = "http://www.dtworkroom.com/doris/1/2.0.0/"; - dio.options.connectTimeout = 30 * 1000; //5s - dio.options.receiveTimeout = 30 * 1000; - dio.options.headers = options.headers; - - // Add request interceptor - dio.interceptors - .add(InterceptorsWrapper(onRequest: (RequestOptions options) async { - String token = await LocalStorage.get('githubRespLoginToken') ?? ''; - if (options.headers['Authorization'] == null) { - options.headers['Authorization'] = 'token $token'; - } - // Do something before request is sent - return options; //continue - // If you want to resolve the request with some custom data, - // you can return a `Response` object or return `dio.resolve(data)`. - // If you want to reject the request with a error message, - // you can return a `DioError` object or return `dio.reject(errMsg)` - }, onResponse: (Response response) { - // Do something with response data - _print('http.dart response success', response); - return response; // continue - }, onError: (DioError e) { - // Do something with response error - _print('http.dart response fail', - '${e.response.data} status: ${e.response.statusCode}'); - // return e; //continue - })); - // dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志 - - return dio; -} - -Future get({url, data = const {}}) async { - try { - return getDio().get(url).then((resp) => resp.data); - } on DioError catch (e) { - return {'code': e.response.statusCode, 'msg': e.response.data}; - } -} - -Future post({url, data = const {}, options}) async { - try { - return getDio(options).post(url, data: data).then((resp) => resp.data); - } on DioError catch (e) { - return {'code': e.response.statusCode, 'msg': e.response.data}; - } -} diff --git a/lib/store/index.dart b/lib/store/index.dart index 5d6cba7..27dd333 100644 --- a/lib/store/index.dart +++ b/lib/store/index.dart @@ -14,9 +14,11 @@ import './models/author_state_model.dart' show AuthorModel; export './models/config_state_model.dart' show ConfigModel; export './models/user_model.dart' show UserModel; export './models/author_state_model.dart' show AuthorModel; +import 'package:flutter/material.dart'; class Store { - static dynamic ctx; + static dynamic storeCtx = null; + static dynamic widgetCtx = null; static init({model, child, dispose = true}) { final providers = Providers() ..provide(Provider.value(ConfigModel())) @@ -29,19 +31,30 @@ class Store { dispose: dispose, ); } + /** + * 设置数据层上下文 + */ + static setStoreCtx(context) { + print('setStoreCtx ============== $storeCtx '); + storeCtx = context; + print('context====================== $storeCtx'); + } - static setContext(context) { - print('setContext $ctx'); - ctx = context; - print('context $ctx'); + /** + * 设置Widget上下文 + */ + static setWidgetCtx(context) { + print('setWidgetCtx ============== $storeCtx '); + widgetCtx = context; + print('setWidgetCtx ====================== $storeCtx'); } /** * 获取 */ static T valueNotCtx() { - print('============valueNotCtx $ctx '); - return Provide.value(ctx); + print('============valueNotCtx $storeCtx '); + return Provide.value(storeCtx); } /** diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index d12a967..0a7b40e 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -1,16 +1,30 @@ import 'package:dio/dio.dart' show Options; import '../objects/user_info.dart' show UserInfo; import '../objects/github_resp_info.dart' show GitHubRespInfo; -import 'package:efox_flutter/store/http.dart' as Http; +import 'package:efox_flutter/http/index.dart' as Http; import 'dart:convert'; import 'package:flutter/foundation.dart' show ChangeNotifier; import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; +import 'package:flutter/material.dart'; class UserModel with ChangeNotifier { UserInfo user = UserInfo(); - $setUserInfo(payload) { - user = payload; - notifyListeners(); + /** + * 登录控制 + */ + Future $loginController(context, payload) async { + dynamic result = $login(payload); + if (result == true) { + Scaffold.of(context).showSnackBar(new SnackBar( + content: new Text('登录成功'), + )); + Navigator.of(context).pop(); + } else { + print('response $result'); + Scaffold.of(context).showSnackBar(new SnackBar( + content: new Text('登录失败'), + )); + } } Future $login(payload) async { @@ -20,7 +34,7 @@ class UserModel with ChangeNotifier { var credentials = base64.encode(bytes); const data = { "scopes": ["user", "repo", "gist", "notifications", "public_repo"], - "note": "admin_script", + "note": "admin_script2", "client_id": "d8eef6133f1a2be3a842", "client_secret": "2b005eed01c72aefd68fac5c5c7f2654f81c227a" }; @@ -30,38 +44,52 @@ class UserModel with ChangeNotifier { data: data, options: options, ); + if (response['data'] != null) { + $setLoginRespInfo(response['data']); + return true; + } else { + // $clearUserInfo(); + return response; + } + } - GitHubRespInfo user = GitHubRespInfo.fromJson(response); - print('GitHubRespInfo $user'); - await LocalStorage.set('githubRespInfo', response.toString()); - await LocalStorage.set('githubRespLoginToken', user.token.toString()); - await $getUserInfo(); + $setLoginRespInfo(payload) { + GitHubRespInfo user = GitHubRespInfo.fromJson(payload); + LocalStorage.set('githubRespInfo', user.toString()); + LocalStorage.set('githubRespLoginToken', user.token.toString()); + $getUserInfo(); // 授权成功获取用户信息 } /** - * 获取用户信息 + * 授权成功或打开app时获取用户信息 */ Future $getUserInfo() async { var response = await Http.post( url: 'https://api.github.com/user', ); - UserInfo user = UserInfo.fromJson(response); - $setUserInfo(user); + if (response['data'] != null) { + UserInfo user = UserInfo.fromJson(response['data']); + $setUserInfo(user); + } else { + $clearUserInfo(); + } } /** - * 校验登录态 + * 设置用户信息 */ - Future $checkAuthentication() async { - String token = await LocalStorage.get('githubRespLoginToken'); - if (token == null) { - return false; - } - ; - const client_id = 'd8eef6133f1a2be3a842'; - var response = await Http.get( - url: 'https://api.github.com/applications/$client_id/tokens/$token'); - print('response ${response} ${response['code']} ${response['msg']}'); - return true; + $setUserInfo(payload) { + user = payload; + notifyListeners(); + } + + /** + * 清空用户信息 + */ + $clearUserInfo() { + user = UserInfo(); + LocalStorage.remove('githubRespInfo'); + LocalStorage.remove('githubRespLoginToken'); + notifyListeners(); } } diff --git a/lib/utils/appVersion.dart b/lib/utils/appVersion.dart index c44fe71..d8fb513 100644 --- a/lib/utils/appVersion.dart +++ b/lib/utils/appVersion.dart @@ -5,7 +5,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:open_file/open_file.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:efox_flutter/store/http.dart' as Http; +import 'package:efox_flutter/http/index.dart' as Http; import 'package:flutter/material.dart'; class AppVersion { @@ -80,22 +80,25 @@ class AppVersion { } Future checkVersion(String version, String platform) async { + Map d = { + 'version': version, + 'isNew': false, + 'platform': platform + }; var res = await Http.get( url: 'https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/version.json', ); - res = json.decode(res); - print('res=${res['version']}'); - String newVersion = (res['version'] != null) ? res['version'] : version; - //newVersion = '1.0.1'; //debug code - print('$newVersion $res $version'); - bool isNewestVersion = (newVersion == version) ? false : true; - Map d = { - 'version': newVersion, - 'isNew': isNewestVersion, - 'platform': platform - }; - return Future.value(d); + if (res['data'] != null) { + res = json.decode(res['data']); + print('res=${res['version']}'); + String newVersion = (res['version'] != null) ? res['version'] : version; + //newVersion = '1.0.1'; //debug code + print('$newVersion $res $version'); + d['isNew'] = (newVersion == version) ? false : true; + d['version'] = newVersion; + return Future.value(d); + } } Future _downAndInstall(String version) async { diff --git a/lib/utils/loadAsset.dart b/lib/utils/loadAsset.dart index 1ad6ee1..3298b02 100644 --- a/lib/utils/loadAsset.dart +++ b/lib/utils/loadAsset.dart @@ -1,5 +1,5 @@ import 'package:flutter/services.dart' show rootBundle; -import 'package:efox_flutter/store/http.dart' as Http; +import 'package:efox_flutter/http/index.dart' as Http; Future readLocaleFile(path) async { return await rootBundle.loadString('${path}', cache: false); From 720564c92812f26059752ff1ec3d139d65e81f75 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Fri, 29 Mar 2019 22:17:11 +0800 Subject: [PATCH 051/100] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0loading=E6=95=88?= =?UTF-8?q?=E6=9E=9C=EF=BC=8C=E5=BE=85=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/controller/index.dart | 10 +- lib/http/index.dart | 31 ++++--- lib/http/loading2.dart | 64 +++++++++++++ lib/http/loadingDialog.dart | 151 +++++++++++++++++++++---------- lib/main.dart | 1 - lib/page/component/tabs.dart | 12 +-- lib/page/home.dart | 4 - lib/router/controller.dart | 0 lib/store/index.dart | 5 - lib/store/models/user_model.dart | 2 +- 10 files changed, 197 insertions(+), 83 deletions(-) create mode 100644 lib/http/loading2.dart create mode 100644 lib/router/controller.dart diff --git a/lib/controller/index.dart b/lib/controller/index.dart index 94ab327..02deea6 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -1,8 +1,14 @@ -import 'package:efox_flutter/store/index.dart' show Store, ConfigModel, UserModel; +import 'package:efox_flutter/store/index.dart' + show Store, ConfigModel, UserModel; + +import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; void initState() { // 获取版本号 Store.valueNotCtx().$getAppVersion(); // 登录 Store.valueNotCtx().$getUserInfo(); -} \ No newline at end of file + Future.delayed(Duration(seconds: 3), () { + AppVersion().check(Store.widgetCtx); + }); +} diff --git a/lib/http/index.dart b/lib/http/index.dart index 7eb988f..aa104f7 100644 --- a/lib/http/index.dart +++ b/lib/http/index.dart @@ -10,10 +10,9 @@ import 'package:dio/dio.dart' import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; import 'log.dart' show log; import 'package:efox_flutter/store/index.dart' show Store; -import 'package:flutter/material.dart'; -import 'loadingDialog.dart' show showAppLoading; +import 'loadingDialog.dart' as AppLoading; -Dio getDio([Options options]) { +Dio getDio({options, loading}) { if (options == null) { options = Options( headers: { @@ -35,9 +34,7 @@ Dio getDio([Options options]) { if (options.headers['Authorization'] == null && token != null) { options.headers['Authorization'] = 'token $token'; } - print( - ' Store.widgetCtx -------------------------------- ${Store.widgetCtx}'); - showAppLoading(); + await AppLoading.beforeRequest(options.uri, loading); log('【发送请求】 ${options.uri} ', '${options.headers} ${options.data}'); // Do something before request is sent return options; //continue @@ -49,11 +46,13 @@ Dio getDio([Options options]) { onResponse: (Response response) async { log('【请求成功】 ${response.request.uri},【状态码 ${response.statusCode}】', response); - showAppLoading(); - return {'data': response.data}; // continue + return Future.delayed(Duration(seconds: 3), () async { + await AppLoading.afterResponse(response.request.uri, loading); + return {'data': response.data}; // continue + }); }, onError: (DioError e) async { - // showAppLoading(); + await AppLoading.afterResponse(e.request.uri, loading); dynamic msg = e.message; dynamic code = 0; // 错误码 dynamic status = e.type; // http请求状态 @@ -67,16 +66,20 @@ Dio getDio([Options options]) { }, )); dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志 - + return dio; } -Future get({url, data = const {}}) async { - return getDio().get(url).then((resp) => resp.data); +Future get({url, data = const {}, options, loading}) async { + return getDio(options: options, loading: loading ?? Map()) + .get(url) + .then((resp) => resp.data); } -Future post({url, data = const {}, options}) async { - return getDio(options).post(url, data: data).then((resp) { +Future post({url, data = const {}, options, loading}) async { + return getDio(options: options, loading: loading ?? Map()) + .post(url, data: data) + .then((resp) { return resp.data; }); } diff --git a/lib/http/loading2.dart b/lib/http/loading2.dart new file mode 100644 index 0000000..32985fa --- /dev/null +++ b/lib/http/loading2.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:efox_flutter/store/index.dart' show Store; + +OverlayEntry overlayEntry = null; +var count = 0; +/** + * loading: 可配置参数 + * requestOrComplete: 是否发送请求或已完成 true表示发送请求需要开启loading,false表示完成请求可关闭loading + */ +void showAppLoading(loading, requestOrComplete) { + // showDialog( + // context: Store.widgetCtx, + // builder: (context) { + // return Center( + // child: CircularProgressIndicator( + // // value: 0.5, + // semanticsLabel: 'hsh', + // semanticsValue: 'sds', + // ), + // ); + // }); + if (requestOrComplete) { + ++count; + } else { + --count; + } + if (count >= 1 && overlayEntry?.maintainState == true) return; + if (overlayEntry?.maintainState == true && + requestOrComplete == false && + count == 0) { + print('移除啦~~~'); + overlayEntry.maintainState = false; + overlayEntry.remove(); + return; + } + // var overlayState = Overlay.of(Store.widgetCtx); + + Navigator.of(Store.widgetCtx).push(MaterialPageRoute(builder: (context) { + return Scaffold( + backgroundColor: Colors.transparent, + // body: OverlayEntry(), + ); + })); + overlayEntry.maintainState = true; + + // Navigator.of(Store.widgetCtx).push(MaterialPageRoute(builder: (context) { + // return Index(); + // })); +} + +// class Index extends Dialog { +// @override +// Widget build(BuildContext context) { +// final ThemeData theme = Theme.of(context, shadowThemeOnly: true); +// return Scaffold( +// backgroundColor: Colors.transparent, +// body: Center( +// child: Column( +// children: [CircularProgressIndicator(), Text('loading...')], +// ), +// ), +// ); +// } +// } diff --git a/lib/http/loadingDialog.dart b/lib/http/loadingDialog.dart index 063a035..f172698 100644 --- a/lib/http/loadingDialog.dart +++ b/lib/http/loadingDialog.dart @@ -1,64 +1,119 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/store/index.dart' show Store; -OverlayEntry overlayEntry = null; -void showAppLoading() { - // showDialog( - // context: Store.widgetCtx, - // builder: (context) { - // return Center( - // child: CircularProgressIndicator( - // // value: 0.5, - // semanticsLabel: 'hsh', - // semanticsValue: 'sds', - // ), - // ); - // }); - if (overlayEntry?.maintainState == true) { - print('移除啦~~~'); - overlayEntry.maintainState = false; - overlayEntry.remove(); - return; +bool loading = false; +Set dict = Set(); + +void beforeRequest(uri, Map options) { + dict.add(uri); + if (loading == false) { + showAppLoading(options); + loading = true; + } +} + +void afterResponse(uri, Map options) { + dict.remove(uri); + if (dict.length == 0 && loading == true) { + Navigator.of(Store.widgetCtx, rootNavigator: true).pop('close dialog'); + loading = false; } - var overlayState = Overlay.of(Store.widgetCtx); - overlayEntry = new OverlayEntry(builder: (context) { +} + +/** + * loading: 可配置参数 + * requestOrComplete: 是否发送请求或已完成 true表示发送请求需要开启loading,false表示完成请求可关闭loading + */ +void showAppLoading(Map options) { + options = { + 'notLoading': options['notLoading'] ?? false, + 'text': options['text'] ?? 'loading...' + }; + showDialog( + context: Store.widgetCtx, + builder: (context) { + return LoadingDialog(text: options['text']); + }, + ); +} + +class LoadingDialog extends StatefulWidget { + final String text; + LoadingDialog({Key key, @required this.text}) : super(key: key); + + @override + LoadingDialogState createState() => LoadingDialogState(); +} + +class LoadingDialogState extends State { + @override + Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, - body: GestureDetector( + body: Center( child: Column( - crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, - children: [CircularProgressIndicator(), Text('loading...')], + children: [ + SizedBox( + child: CircularProgressIndicator( + strokeWidth: 5, + ), + height: 50, + width: 50, + ), + Divider( + height: 10, + ), + widget.text != null + ? Text( + widget.text, + style: TextStyle( + color: Theme.of(context).primaryTextTheme.title.color), + ) + : '' + ], ), - onTap: () { - print('移除啦~~~ ${overlayEntry.maintainState}'); - overlayEntry.maintainState = false; - overlayEntry?.remove(); - print('overlayEntry ${overlayEntry}'); - }, ), ); - }); - // 加载框 - overlayState.insert(overlayEntry); - overlayEntry.maintainState = true; + } - // Navigator.of(Store.widgetCtx).push(MaterialPageRoute(builder: (context) { - // return Index(); - // })); + @override + void dispose() { + super.dispose(); + loading = false; + dict.clear(); + } } -// class Index extends Dialog { -// @override -// Widget build(BuildContext context) { -// final ThemeData theme = Theme.of(context, shadowThemeOnly: true); -// return Scaffold( -// backgroundColor: Colors.transparent, -// body: Center( -// child: Column( -// children: [CircularProgressIndicator(), Text('loading...')], +// var overlayState = Overlay.of(Store.widgetCtx); +// overlayEntry = new OverlayEntry(builder: (context) { +// return Scaffold( +// backgroundColor: Colors.transparent, +// body: SizedBox.expand( +// child: Container( +// decoration: BoxDecoration( +// color: Color.fromARGB(100, 255, 255, 255), +// ), +// child: GestureDetector( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.center, +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// CircularProgressIndicator(), +// Text('loading...') +// ], +// ), +// onTap: () { +// print('移除啦~~~ ${overlayEntry.maintainState}'); +// overlayEntry?.remove(); +// loading = false; +// print('overlayEntry ${overlayEntry}'); +// }, // ), // ), -// ); -// } -// } +// ), +// ); +// }); +// 加载框 +// overlayState.insert(overlayEntry); +// overlayEntry.maintainState = true; diff --git a/lib/main.dart b/lib/main.dart index 3364c80..796e775 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,7 +17,6 @@ class MainApp extends StatefulWidget { @override MainAppState createState() => MainAppState(); } - class MainAppState extends State { // 定义全局 语言代理 AppLocalizationsDelegate _delegate; diff --git a/lib/page/component/tabs.dart b/lib/page/component/tabs.dart index a41d086..a64bf2e 100644 --- a/lib/page/component/tabs.dart +++ b/lib/page/component/tabs.dart @@ -123,14 +123,10 @@ class _IndexState extends State ], ), onPressed: () { - // FluroRouter.router.navigateTo( - // context, - // nameSpaces + _tmpWidgetList[index].title, - // ); - - Scaffold.of(context).showSnackBar(SnackBar( - content: Text('haha'), - )); + FluroRouter.router.navigateTo( + context, + nameSpaces + _tmpWidgetList[index].title, + ); }, ), ); diff --git a/lib/page/home.dart b/lib/page/home.dart index 5998b1a..4939d3f 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -6,7 +6,6 @@ import 'component/tabs.dart' as TabIndex; import 'mine/index.dart' as MyIndex; import 'app-login/index.dart' as LoginIndex; -import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; import 'package:efox_flutter/store/index.dart' show Store, UserModel; class Index extends StatefulWidget { @@ -21,10 +20,7 @@ class _IndexState extends State { void initState() { super.initState(); _pageController = PageController(); - print('==============home context $context'); - // Store.setContext(context); Controller.initState(); - AppVersion().check(context); } @override diff --git a/lib/router/controller.dart b/lib/router/controller.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/store/index.dart b/lib/store/index.dart index 27dd333..4872213 100644 --- a/lib/store/index.dart +++ b/lib/store/index.dart @@ -35,25 +35,20 @@ class Store { * 设置数据层上下文 */ static setStoreCtx(context) { - print('setStoreCtx ============== $storeCtx '); storeCtx = context; - print('context====================== $storeCtx'); } /** * 设置Widget上下文 */ static setWidgetCtx(context) { - print('setWidgetCtx ============== $storeCtx '); widgetCtx = context; - print('setWidgetCtx ====================== $storeCtx'); } /** * 获取 */ static T valueNotCtx() { - print('============valueNotCtx $storeCtx '); return Provide.value(storeCtx); } diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index 0a7b40e..d77ec28 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -13,7 +13,7 @@ class UserModel with ChangeNotifier { * 登录控制 */ Future $loginController(context, payload) async { - dynamic result = $login(payload); + dynamic result = await $login(payload); if (result == true) { Scaffold.of(context).showSnackBar(new SnackBar( content: new Text('登录成功'), From da113d32e98202f67bbe1e4f5ef1b5b97531087d Mon Sep 17 00:00:00 2001 From: sam Date: Sun, 31 Mar 2019 18:45:59 +0800 Subject: [PATCH 052/100] feat: run --- ios/Runner.xcodeproj/project.pbxproj | 33 ++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 7458b08..3b883ab 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -43,6 +43,8 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 3DB87B9B20AEA710C58FBCC8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 6F6754CA16FD7E3C9E4FCEB3 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -56,6 +58,7 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BEBBA521223FEA6600583D52 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + C1D6F379F6B3C85C2CC34999 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; D7BE9010C4F7FF033BA299B7 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -84,6 +87,9 @@ 8B5D251E963699100C93BD5E /* Pods */ = { isa = PBXGroup; children = ( + 6F6754CA16FD7E3C9E4FCEB3 /* Pods-Runner.debug.xcconfig */, + 3DB87B9B20AEA710C58FBCC8 /* Pods-Runner.release.xcconfig */, + C1D6F379F6B3C85C2CC34999 /* Pods-Runner.profile.xcconfig */, ); name = Pods; sourceTree = ""; @@ -160,6 +166,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, EB6D2D2E068567EE260A8427 /* [CP] Embed Pods Frameworks */, + B44D8CA51437C704F51F9F97 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -266,13 +273,35 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + B44D8CA51437C704F51F9F97 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/flutter_downloader/FlutterDownloaderDatabase.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FlutterDownloaderDatabase.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; EB6D2D2E068567EE260A8427 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; @@ -281,7 +310,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ From 8d2cbbea963abfad7c914188b635186c01abaec6 Mon Sep 17 00:00:00 2001 From: sam Date: Sun, 31 Mar 2019 19:32:51 +0800 Subject: [PATCH 053/100] feat: run --- ios/Runner.xcodeproj/project.pbxproj | 33 ++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 7458b08..64cb2f4 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -39,8 +39,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 13BE6F8BF79FED0958E617CA /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 15BE2897D1518D9BDD5AF970 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -57,6 +59,7 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BEBBA521223FEA6600583D52 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; D7BE9010C4F7FF033BA299B7 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + FDCB5E5FC17434D1FF8247D2 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -84,6 +87,9 @@ 8B5D251E963699100C93BD5E /* Pods */ = { isa = PBXGroup; children = ( + FDCB5E5FC17434D1FF8247D2 /* Pods-Runner.debug.xcconfig */, + 13BE6F8BF79FED0958E617CA /* Pods-Runner.release.xcconfig */, + 15BE2897D1518D9BDD5AF970 /* Pods-Runner.profile.xcconfig */, ); name = Pods; sourceTree = ""; @@ -160,6 +166,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, EB6D2D2E068567EE260A8427 /* [CP] Embed Pods Frameworks */, + CFD61D2B0A4C5DCF2308F823 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -266,13 +273,35 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + CFD61D2B0A4C5DCF2308F823 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/flutter_downloader/FlutterDownloaderDatabase.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FlutterDownloaderDatabase.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; EB6D2D2E068567EE260A8427 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; @@ -281,7 +310,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ From c0653334b024b2d3483cee2ccda2187bcfca9b55 Mon Sep 17 00:00:00 2001 From: sam Date: Sun, 31 Mar 2019 19:42:38 +0800 Subject: [PATCH 054/100] feat: stepper --- lib/widget/common/stepper/demo.dart | 55 +++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/lib/widget/common/stepper/demo.dart b/lib/widget/common/stepper/demo.dart index efdfcbc..465b536 100644 --- a/lib/widget/common/stepper/demo.dart +++ b/lib/widget/common/stepper/demo.dart @@ -6,6 +6,7 @@ class Index extends StatefulWidget { } class _IndexState extends State { + int _currentStep = 0; @override void initState() { super.initState(); @@ -17,9 +18,57 @@ class _IndexState extends State { appBar: AppBar( title: Text('Stepper'), ), - body: Center( - child: Text('更新中'), - ), + body: Container( + padding: EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Theme( + data: Theme.of(context).copyWith( + primaryColor: Colors.black + ), + child: Stepper( + currentStep: _currentStep, + onStepTapped: (int value) { + setState(() { + _currentStep = value; + }); + }, + onStepContinue: () { + setState(() { + _currentStep < 2 ? _currentStep += 1 : _currentStep = 0; + }); + }, + onStepCancel: (){ + setState(() { + _currentStep > 0 ? _currentStep -= 1 : _currentStep = 0; + }); + }, + steps: [ + Step( + title: Text('Login'), + subtitle: Text('Login first'), + content: Text('Confirm your information'), + isActive: _currentStep == 0 + ), + Step( + title: Text('Choose plan'), + subtitle: Text('Choose your plan'), + content: Text('Confirm your information'), + isActive: _currentStep == 1 + ), + Step( + title: Text('Confirm payment'), + subtitle: Text('Confirm yoir payment method'), + content: Text('Confirm your information'), + isActive: _currentStep == 2 + ), + ], + ), + ) + ], + ), + ) ); } } From 850b7cef9c3bab6f3525c246097e87aa7b19e547 Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 2 Apr 2019 21:13:12 +0800 Subject: [PATCH 055/100] feat: Stepper --- docs/widget/common/stepper/index.md | 30 +++++++- lib/components/widgetComp.dart | 1 + lib/widget/common/stepper/demo.dart | 103 +++++++++++++++++++++++++--- readme/widget_progress.md | 2 +- 4 files changed, 124 insertions(+), 12 deletions(-) diff --git a/docs/widget/common/stepper/index.md b/docs/widget/common/stepper/index.md index 83532f6..2a8c50a 100644 --- a/docs/widget/common/stepper/index.md +++ b/docs/widget/common/stepper/index.md @@ -1 +1,29 @@ -## **文档完善中** \ No newline at end of file +## **Stepper** + +> +一个步骤小控件,通过一系列步骤显示进度,对于表单的情况,Stepper特别有用,可以通过控制需要完成多个步骤才能提交的表单 + +### 构造方法 +``` dart +Stepper({ + Key key, + @required this.steps, + this.physics, + this.type = StepperType.vertical, + this.currentStep = 0, + this.onStepTapped, + this.onStepContinue, + this.onStepCancel, + this.controlsBuilder, + }) +``` + +### 属性介绍 +* steps: Stepper的内容,包含标题,副标题,内容 +* physics: Stepper滚动视图应如何响应用户输入 +* type = StepperType.vertical: 确定Stepper的布局类型 +* currentStep: 当前步骤索引,内容被显示 +* onStepTapped: step被点击时的回调 +* onStepContinue: 点击 继续 按钮时的回调 +* onStepCancel: 点击 取消 按钮时的回调 +* controlsBuilder: 用于创建自定义控件的回调 \ No newline at end of file diff --git a/lib/components/widgetComp.dart b/lib/components/widgetComp.dart index dbdcf33..67f0a39 100644 --- a/lib/components/widgetComp.dart +++ b/lib/components/widgetComp.dart @@ -79,6 +79,7 @@ class IndexState extends State { this._bodyList.add(authorTile(nameKey)); this._bodyList.add(Divider()); } + print('文档完成长度:${mdText.length}'); if (mdText.length > 30) { this._bodyList.add(await MarkDownComp.Index(mdText)); // demo diff --git a/lib/widget/common/stepper/demo.dart b/lib/widget/common/stepper/demo.dart index 465b536..53bf075 100644 --- a/lib/widget/common/stepper/demo.dart +++ b/lib/widget/common/stepper/demo.dart @@ -7,6 +7,11 @@ class Index extends StatefulWidget { class _IndexState extends State { int _currentStep = 0; + // final _textEditingController1 = TextEditingController(); + // final _textEditingController2 = TextEditingController(); + // final _textEditingController3 = TextEditingController(); + List _textEditingController = [TextEditingController(), TextEditingController(), TextEditingController()]; + @override void initState() { super.initState(); @@ -25,9 +30,11 @@ class _IndexState extends State { children: [ Theme( data: Theme.of(context).copyWith( - primaryColor: Colors.black + primaryColor: Theme.of(context).primaryColor ), child: Stepper( + physics: NeverScrollableScrollPhysics(), + type: StepperType.vertical, currentStep: _currentStep, onStepTapped: (int value) { setState(() { @@ -46,29 +53,105 @@ class _IndexState extends State { }, steps: [ Step( - title: Text('Login'), - subtitle: Text('Login first'), - content: Text('Confirm your information'), + title: Text('Name'), + subtitle: Text('User name'), + content: Container( + padding: EdgeInsets.all(10.0), + child: TextField( + controller: _textEditingController[0], + maxLength: 10, + style: TextStyle( + fontSize: 20.0, + color: Theme.of(context).primaryColor + ), + // autofocus: true, + decoration: InputDecoration( + icon: Icon(Icons.people), + labelText: 'Name', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(15.0) + ) + ), + ), + ), isActive: _currentStep == 0 ), Step( - title: Text('Choose plan'), - subtitle: Text('Choose your plan'), - content: Text('Confirm your information'), + title: Text('Phone'), + subtitle: Text('User phone'), + content: Container( + padding: EdgeInsets.all(10.0), + child: TextField( + controller: _textEditingController[1], + maxLength: 18, + style: TextStyle( + fontSize: 20, + color: Theme.of(context).primaryColor + ), + keyboardType: TextInputType.phone, + decoration: InputDecoration( + icon: Icon(Icons.phone), + labelText: 'Phone', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(15.0) + ) + ), + ), + ), isActive: _currentStep == 1 ), Step( - title: Text('Confirm payment'), - subtitle: Text('Confirm yoir payment method'), - content: Text('Confirm your information'), + title: Text('Email'), + subtitle: Text('User email'), + content: Container( + padding: EdgeInsets.all(10.0), + child: TextField( + controller: _textEditingController[2], + maxLength: 20, + style: TextStyle( + fontSize: 20, + color: Theme.of(context).primaryColor + ), + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + icon: Icon(Icons.email), + labelText: 'Email', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(15.0) + ) + ), + ), + ), isActive: _currentStep == 2 ), ], ), + ), + RaisedButton( + color: Theme.of(context).primaryColor, + onPressed: _openSimpleDialog, + child: Text('Click on me to show information',style: TextStyle(color: Colors.white),), ) ], ), ) ); } + + Future _openSimpleDialog () async { + showDialog( + context: context, + builder: (BuildContext context) { + return SimpleDialog( + title: Text('User Information'), + titlePadding: EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0), + contentPadding: EdgeInsets.fromLTRB(24.0, 12.0, 0.0, 16.0), + children: List.generate(3, (index) { + return Text('${_textEditingController[index].text}'); + }), + ); + } + ); + } + } diff --git a/readme/widget_progress.md b/readme/widget_progress.md index 80cc90e..249a773 100644 --- a/readme/widget_progress.md +++ b/readme/widget_progress.md @@ -60,7 +60,7 @@ Flutter UI │ ├─placeholder │ ├─raisedbutton │ ├─rawimage - │ ├─stepper + │ ├─stepper 【✔️ v1.0】 │ ├─text 【✔️ v1.0】 │ └─tooltip ├─form From 4485c720143597937487a3d390044e1aeee93844 Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 3 Apr 2019 15:47:46 +0800 Subject: [PATCH 056/100] feat: Image --- assets/imgs/cool.jpg | Bin 0 -> 23261 bytes docs/widget/common/image/index.md | 52 +++++++++++++++++++++++++- lib/widget/common/image/demo.dart | 59 ++++++++++++++++++++++++++++-- readme/widget_progress.md | 2 +- 4 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 assets/imgs/cool.jpg diff --git a/assets/imgs/cool.jpg b/assets/imgs/cool.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2ed7854e8af40c594f52b3a1e0f3f4be71d76da2 GIT binary patch literal 23261 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 MY!md z06r@aSg5{Dilo{eD!rnSTPwi6`Y4`*_YGbKfS z6r3}z!y`tXu|&UsWXLGr$&h-LMK3#gKE07AS@!7qG2KQki4dlM$rcEUWCAkF zYd6TtQDAGuXR~>oJP;GA;GI}^Q~M*uH6(PQ?@NXWD#M~U8C9B*+N3oTX+(}Gjq zoPJTv5uNHbWbFLFYrXGJnj8$HjP9i_|LH~@Fr*Ny4!z+tQ>$V^kiVXWSFU*^q1MvU zovyoJzfgDv72;pSM*LejFBDTwXh4kGN~%}tyz3UTRmS*cZ>(F9^JaFNW-xq@ABH{@ z1=@H8^-hpE&S-F!MVUTKXcg4UVI#9q4FhYRQ6(GDXEI3OozlnRUDxk`+|!lXqkNTK zJiz`Y&l2kX=LspuyR>Y3pPl2Tik6|)w_@hWS_hvyMqP~z%yB274a`Vpxnc=!M5gNO zZyqN`6~(SYj3ZxOH)W9AQyf@d{`x&<%);I{hESCBnixG$p?Cr(+H~oaiIAq%f3nh? zgPmX`CboSar2M>0rWsNr)~N`jTyQT+M8)+SSe1dni>{6bfzwAq8Yoj)K%k9|87GJu8^_;R(jaPgnIXw}r9V;sq@Y8C+^rgqlwX3lr z*9`j;kQ7Vhry*~}ew}qf3mDqSLg)49rtE%#ImABel_E~4ahDuVCfO+T-KQAT$xt-I zL-2gCnaS^IY7pL434&Se{2^yvq#dFXGF1+^elaY4q z6=z04d=CUeS$yN3e*=e?2*irHVH5Qw|5&8{aE01qcL3SmU6!F%!wE5pjyT_3N{j?g zOsn{Xq!iOC(el1eptw&kd5&$`hU0!#!~ji8QD7j?-GQ6b zot;>1jL}Y~NK8pvw}vc-;6P;XMN(39M|2kQ)<=p38$2cw8_t@NTf>YyJ}5pXQRZD@ zQ%WsqHh*BtQWk@K#bsgrQC0lBc|G~^ZRst?9yo|;!#O)jBLRnA5)#S>l@cpIv|uJd zq8A&lw5uNv>4`p{Z(PR43ne2exxAQoB4fACd`p7J_)#Qg8C*&gEsDfVx4P$EnCk52 z*|PZ*H#1B4HG@PbfG<1Q|JOPO0zgIqMI~YqBxV*;{BP<&|C>6p$^fq;(D8_zpaZV> z9yLomc1t|U3ouyy6+H4Q1g1JMq#W{d*f2H4FtPH$qQ5QDZAxBSPUNA?LO+04f;Yn{ zFo)lV;#gCt3{iGm@=%?zm0Eh3W@C!ARFA2N5yC;sXUxhiJuMYOa-pbf0yStCXdb9? z!?7j64*eUrXDJD55nc|Fz9<$z{-PKJ0sk#rE+rLbBD8$I9%tA_FBrHZwOhJi_ zXhed-iptJ~tj2@a;H1Gx3q?&gDuyN@`~Q0&ffV>Ukn~lH<=3hu`yV;=ns~Y>^CR#k zI$bHQ?1U;5m#gVzDh1823z&J0Re$rW;yQdupXNX1HOe3C(>Epk*ek3F%WYxP$W0@t z{vk>jtY=TF!61Tu0>46wlY`!6Ar?fq6p{v_9H;2i8nWle%wfNuwKFXCQ+NF)|Gs); z$7^&{d2(h)5)uF~a-YQbVo*f!RU3493%cmPn7y{=cm{jDNlSjk*hhYqQ4O**w z_XsmLu?4NP0BA0n1g#rf)d-dS=~9KmBW-Z7pHxHH9n_xuY48E`AJB@$z=WMtW$B$_ ze?6snbo{I7FpBXpGC_HV zvPoIR4npLaau&Z!AHXJ_gS8A;|5$Py#aQTWvgmQqVZN=4-1diPIWzsmB(FnTj$AKm z`UzN&G~-HDuLW+Y|Co%1HrMnwJ%L?7|5Nb>9VVSVnm~ks)4j_j}w!U z2!^5I;3f*dgUL+AA`Fx!REs$lOWcYB?jW{fY1E_qL~Mf=w$>=_tr^-K21g`U@2^5@ zVf}sNnW7RvmCc`|zTCAJ1d0hrsPgzwuJTER*gEeiRf1hnd@)0oP>gtX)S(2E87PYd zFT2Cge6_?I^64BR50J~2SNyXZ421%ASptbtx)@)w9$=M%u&*MeJ4-H&I8fXJ3ceHy z^-H5iO>H_jIZ}|Xa5(FY6c6uU`j3z1OK=CfggKL8ZK!4CAL#dYAoW{gUMp;> zE{U3H=4@nGf6-vj$V8sED>x}23vrA6f^KJ*qGn2}#UVk?n@z54+om2T4cYRMeV=WFgAyjcVe2 zRp&`>0G&v7u+I;LhahE@UP{cx{|Qm&aN8T6==0uPE<+1A%8m%81-iWj@qr0g*&LPwd@Q+*)dO8R!YFb z<{+A^oyRRK+DjS<_1IPYA`pI7a5^q{#Lo3Ic0VnXgK#r;74{#e$ZgDWF%W4h|Z zzI?4lMCXf!2NH)`?enUE`b{L>x!6}t64qr|c}^z1|HCf^><52Is#}@TuL)s)&1+q% zZgRh4yi0YmM;fo9-35^zB^2GUDrP7!hY|D9I59V=@KfI>349@ z4?h9%Z=0?@BNBfegJTkHa+z;K$%|jAP4*BmaN5QbZ>!9IEE%;By5f0i^{6@Ntj}dt zH0H}CUx)==GVT9r-}}20n*tU&2?&gIiLkabFDLM%rbA^@Ndk3RZ=cbKP+Y@fh9;1y z5FU3GITI*N8N!@9QzGK3o8d}=+p_i|>3^urv?)N7QdIHbF5Iv`s2f@y2ed!t_)pmm zCKn=2z~;b8K0*s;__PP3B zN&aK{5H$^ut#-MaLWL-li0`5MGs5evm8uO~CVL&y6VyFbx#&W6;Q|ZLP8C{Qjx-Zj zIwcsgSaWRX1VcIWOz3PLl}YC<3@;H=KKvCH*}4XN;RFK3dJ z@*dtqTD}R}G!6bWEHJjj{JBdfouU^v^ZPoinMa;gKaHm<7EPU4?8{;s{Q> zt@4zqb+gn7Wvd^Z2H7Sxo71LyEqN<{-aWCDt~b34c@^V<-~S@HLUFl;MjrMP5bWZW z0&<{(kk7^UO)%04tje9CPHNLGTW}|WU1#pvV0y=DmHe#Dm&tBJMf9QNCAQ|cog)rr z@ZM8@4}|#QIjFm54&r| zO(RzCdnzu)PRyrAKh28P%(3i`U5=T(CRn?>?2U#3A{Ao6eSq zR=~M&wY@3?e)vzG4U^>L*~CRXa&PQb&^E#Y_1pAl4ElaXP7?_wo_}YX_gk4A1hz6q z0ZdVJ1a{OP^LgwEj$HT7LL$u4eS!V#Qx;3X_K^QH+ z&NSN04?E5KGiLwR5#{I!-Im=kWpGtdfB##9_uKrzoPSa>u|gwENS-ij?V9GSczj#e zbLTh<5+2R)kL;PJ0jS+H3UBMRK zMkmk+)aN{j^5)^IUkW)b@|Obs0egyMuFyw-vk6!?&h7e2c zx-R&e+Q3}c5JN{<02_G2LOP(b2bbx}#flK^j|h#Le*dPhgEYOI>iS}a^IJk|?uBy@ zj=q7`tvAqX+EB|kPqct|XTKk=v7dwM_H}^e*D?<))yBYT4<9_Q1n-VZ{|j-vz-(e_ zCh9Ot@TAN)W=7iSWe}>kzjSr3tyX=S%CdhEcJ|=YlhTZJ4u){1Tksv7b4g`cgvTC%M0rM#%9^K57rkwxtx&-_ z_?;|-SQ5a9?R)}Gzl}`$;V1n~_69bq;FYZ4-q$QG;OBJLETp*i8XLIxmT_?)mvZKm ztIHvXklWv|E+6-^0O0N1UY|Yz!cVJP>BXKa`0!{F>iBGk40g0(2siL*^y%5qF zbJLaxZq$Q73PTI6q~EkQ-V{G<`{GKLk)tjgqvj1>pzwW>P&BeJfk@P1I|&nixtg7g zV9%Ejtp*PWraI$!1?`J%twl zO3ZLgUArUP@eN6}2{S8^U$~;dN{o_~>ecC=qZ*E$(@Lke?2VS8-oZ4&veunAtLeXVWnk-Kz&K3p?;W$2+2j`d*v%X8ewu-T4_&oVA7vX=s3 zA3kw?*o15kd4F9^k7<91&y2NTY#y6q0CvoD#kTGI@3U=UPP!lHn&C-8hr)?$&~#$7 zG&!C}2WjIZe8B-rh32YW(#Ajm!qd)>{3K0oX`1dEDNs7vq)JZ`NPkJw}DUk-) zv6i#OFs5&&aQdOLr)s_IZ)gnCaWsMvN+!I9Z^L|Tk6VR4Xc_FT+N|2+d}wbcPc~1S zmkl;fQICx{4L!1?B&}&L=A0%_EoRdlx(p?0oVo20c4ckjC7qYi5)3<nl^&q5bV0i?oMWR>u=sus`^M2oe$|B>lGRfXq;44d2?N& z-ywheVG6_CV0S^8fe2FrZ$&`x_A|H0@_YCMh?@QKGjCT1A>Vsjdmm{wZ`V6_jaF%= zj>A{wPh~RlJC-9FXhSRHlC&@PJUv-a^~2skTXuPR9l5pidrSveYJNv~1P|y`uQP*p zZ9leoQJs>3yd$HvA5FSM62BqQZN#muY_ThEl3TxqnEK#qKGu8EP zFkSA_kUX!!DStcMHyY^lde?YM)!1oz{{*F8DWQ!EL+ z@)7Ck7-^Y^t%@6IkwQw;lDgn#kPDPYv27SRw=FntbTbAQH|iGqRba-S(}$N#R&WVr zms)R75r7NqEEm1t`1T&$A3X<>oIX4B1Z~yNsiWn>-7d83xsV!@R=t)EDhKNJOAAA$ ziEX{4oIF*7LaQnfi*W*EAh`Z?Nw-Ts{n(3{xKEN5rqB4yJ3j$5PYf5wr1B-a>5ptj zDMH5C(i)sJM{DMk4nc~sIO6SX{$@toYy?iJje4fTM&e=R$GUE6a=Ld^x^O96R+#O< z^nez^EfXpR-|_+^cc_BDEEjb9#TR_DokUPKw$T zmu}JY#`+KC9Za{Xn3rLn>LCL`SxNzabNI3}>c=SW>z_xT0P^%qmGpqjK!PrrEOi+} z*^S4dL;XWa>nLd)+rJ!9tA4~a25W7*-k*S&r?3`?hMyI8$Fgx{bxIdjd`R0e%sBbF zA4cky2u>PL;VCf6fo8uh#z#uKkzrx;ekPVSYvr&f{z_Yf&|c_~HsvIk9*n|e$yZa! zVZwwkORTqwSomhF0>zb!^F-TXrU4EalB78k+vrCSFve`zI>KqCIoA4kK+VkEc}6`8 z;m)6oG_V>i=86$8xD{3>9p=$-2b@%Itz8=M-@w!lnU5_9 zNsBe$P%Sdde6RBDkF;+wA`~lIU1bz4@$o9RVqA4_# zRShdrktnukZ!*h%pzwBp2P>z-z4e;361{(!*)xu(QprbMT0Dzji}w;Q3p(iQ{RE6? z?SOa+I_T8ZECa0PnlvP%Gu(Pd5ZU4Ibo;l^FFm3I4|Z$1TlB&YI5>3a!5c3c{-}u4 zwh$&_94Ph`cVFzQAZ_I9+md=#k2ag|5;?kmBu>HP)m+@bh7Tql^NM4T^#58m{8;r0 z)sCYu5Bq))FjNFf6Qa3!E)vS==c{_dh?C;sgmKFrSoQFV0*t#nY_G!3TL1?1nnxq1 zBB*zwu>HQf084PQ@;jlm&5ZebtMI4$Hjy z;=C&T)GS+P2smq2eN0>?4?ivBn>DQu?`RP>)04lqKo?AjfH&9=QMB$DgkMaoM{&7JV68_7- zmjZmSD$E&QN`gGL1PNtKZj~Fc*h}&t_^49m) z66(~utTAT>+yn!BI63u&EO)$dZu#hGQMk2tG|aWtJ|!WDNi}SZM1Cg#^IJD-JiH+= z57JzDRrC`O!UnPLB_d`+CY(;eP6Q5M0*5)B8-C=oJGV#V{REVcNZ!=8u54Sk;2~;xUzF;ng&_eXYB%0=NkpaZ1FL~;5`x6B+OrXbbsa+qW7MR}itm5W$q84Cl^`hol$i;kN@ zGI1Yva6C%)C!IF@tup5_2Vf`a6R}Hq_ZdpZD_6NQvG7qs46J6SS zgeE0CrdSt*nnS7>e`SH^_sd$RV z=YDeN1i#~scl5!LX9r%FEUlcL6I6m|$0CG@!k#Z?=8CmAJdXq6a|h4^Vv@L6!|fRP z0JLeVTxdRA4EesGej2nfy7?uV?GH=5OAAs*whXh0an>8llzhS7;m;WHsZBD0iaR%Dj^l}CnR0ar@QM7C6{p|j=!Nysd0z<3h5 zAQdFDq#v)z6R=}k=8T_TQ>l;h37FIQQ7!)c{HmN~Y+mB+nIdk9;_&Wx#%C0#-+G5{ zTFA9Lq>yHtFTS5SVt!od6HqcC-XS%2LI?XQ#Bfe1^gexMn@<2cZ1fx{GE%8Ulq(Ww0W!0g9gm{cHTZeYhM~2htRR!5n|PIMm@OH=yN_7oI0j%%K0yum+AI1H8x2PwTn1bl#z%;{c4anS^=i#~Wb7Wi8!fIA+XUw~=_Qc|oNS5u+64(U)eK z{Sp6RSlr@BP~LMEypQ!x(P%>~bMXn_g>v&I>Te8{?TR-iT4Vh>BzjTHaT+$%VbEPw z&Gmh_JkmKKNTf9B_6{4YJ|a@2;zXd+LP2DeQtEhk1P6B6Q^JU;R#RB=7XN%Tv$szu zfqv4ct7)~N9dL3&D6m@2{PFr2>7Bti0(`3IgCo>4VKUJ&c<`a>Jz zj77gsIcTt>()(QFn`A0@sU~hti8RLwu2NH<_&z`-7MCmg9H4QA2cO6C6b!wxU?=-{ zTSvUPiqCk%d0{zii<2OdTCR@ehb7L2%N_{t&X)FL=^+A|>KK3FyE%+@yuefNF8L%u zs0A>y=Y|-8LVJ4nL1bE`U%Lj4Q`7`1vi#cQ+7U+>XLAJ^T?w)`mCmL@UwOBcdc72S zWZ>BVgKE&897eQ zOB4L=;8K6@*W_sdXj{ELUcMUHIqgu0vT&rD!U*H@*9dMD_yik7$v9+VPrH5P`-2|b zDQk#c_CTSKCmg~-vFl2QdAZ8dh+`hPcO?WggsrOA9kUFe-RN+;l6s41blPss<-&8U z`9u&1f1cB9L1V+-->ip1QIv~QBJhnD0q|-3{E=n2Sr`TpC7ncsbeu{#Yi-B!Z_IbmRMggtUM_MvNo%l?{|i>zecnPX7`%{doa- zU(|k_9K$@!yY!S(DGf!r=C9FZ^a*!oIWu0d*RMDj@juyi(r`;Khui@1 z`gL&$tPo>!j3pEP&p{5s`92bD^C#*71>^M(-+u6l5Uows3;hqFXQ1X{YngNPa1 zFF{ovM6lB1N8gr58ZK(N%hbc&6t+`@5h2G(ISsnbwy7W=bh`6^^saI*9#O!lf$5JUvc*!Jq)z~v$lwS4FU|vF7ORHGun~o12dkDNHeP|0L6a!y1nk*?_yH<9RZnaIQlmOV$xRq z`X{#Ast(_NOJ!cWK#8_w8>SW9wiSW)98+>%#8U-*1|117g-qUNJ-cVl<1OHemSBNF zo`&Kc`mA93zop)s&oV4F?7`LJvsp)$>0KDd~UrJRu8>4H%ZRX0F*$%>+Y@2@kfu(0W*a3I$J1aXy@rLFShDjH0 zrQ2{2!9b(e^_&`sV8$t#jq|^9n3lucFh`|+P7324IsAFpdVMeRCAW;`4^?nD- z3i6b**7{fJ2oX^xFJ6!q>{J<}6f~XDFgMgo7qMwgcLUn@A zU*_M>aT?O^3<`NwmT9==e@gl{c|C`W$%^4whv9ktYr}j+#FyNe4+YOWT*orSz_ba^ zyi`M$oPu#?_}>fZ9KV{4>-*pqtCemN4a0W~`!WOJnWpTVY~023@+(5{{-K-tLdR$z zIR(s9Q;UlGhwBi5X_$_Oua*Y|>;OH2=@9V?^U1(2(IINUp?8*aU8T8Wm+C7>d~2A|Do(U zY+%>OsHizi$B+*M)-esour{&~70c@=N=$xho)M<|>cz6UFUW5ru5y^;2wI%?#xWkl zbiZP}Jd2HVDrtT@t(j#!4Bs$*S+0X{p!DZrjLAF`%)Q#sc{m^-b+Sq<;snh7Mkx$u z04(N0uKi8GMoHhT;g^IZO1&BiPe@J6Bl-{b-XL{DBWn4#R zEv^$|N1-uHbNVM+-d6cR5LjQvzPW($hiRhWJL?#%qBIV8>hvat;Xh}2<{pL=uW zf6Yg!E>72O%$-(#*9X?=Em1dRt|OUAqQ~O&oN?N_!b$9vdFeV=9qJ5&mIqwtdKPNF8rTkBcY7pVUJY!_CiQ%h!fA_n>u+imPTmw(-;)tioaA#Ot_e-n^ zD9`ch$1cK6lW)u_i^L|^+Ee;Rf9HN&v#qB`(i1`#(^0&o4bf%B+N(cSdhx@@Z(c-J zTiNZor2D4x1*8i+=Ky7eb6NlSyiKpMPrzaQHRfCg;?RLee6U#>D?d_5F8wgC0@yO2 zQycQ$#970kN5G8R*~=%O(t!+!%Zof5!I^lGdGvTK;E2>QJGvdILD@-~|N8Em!S#q| zdu^I>|ATrT@=f4>B7ndEU()j>BL9idml%PdAb;8KiN1vBe}kv~MF4$?4?-3)ee4Vg zEHM5lN5;g}E;X`8KzV`Y16@;YNT`Y)GtXcsJi^CVGjuG-;Ysse(m35q#)8m$T%t(s zZhwUG4dSL`lKqM7bOY~f=5>XKXo=)Sr-jm*qN6U~douPk9>S`BNR_u*Bp`TR{(Zvo z$2!yY4FcPtqSbtEd9b~a)dl#+2!B(DHwksPT$|g1VMu`6Ir3==4&gi2+mkSiIAd*> zqqh(nUm3e%3JO2kf*&=)u@NQ$Hm|3stTK9y@yjB@;b1D1rSzVdD0^C8*bfB}8)RKR zIo2F%f?QSYDb2H?w?qVZNxdf=_UOHB*|IW1X2^yd)?PsqAg*e93q4!o~`1_ z#v{14$>jS2;-~4OCOxDXa!QU~1~nWlMYdjXeg6WuXuiBJAYfqN|5kwi^}e70{>S_B z&-sEpxNuFxv@dw`KcQS-ZT~T{%)k65{tWmDXs=%txc-5v4qh)D82FO{#U#t28JDgA znzZsEsVr6^g+#+p&yyOZG*Lj&I6CV1PXPuAMh1^DRFMZ&PNgu1dXeQP;HD|MbjkE} z{xVep7Sn)EIl+^ecQWHRqQ2&Mm#a{|54i592$_ezouwwho}snz32-kdgO|;N5;@^4 z-iGcql9K*$_#&m@S}+sRXg}a-DQDS39#`nnbaOxV_vB*y4Go=~B$}8^bo6C@LMYrN zW$V+5Ae%x$hgX?wURKg}m%y$U+7|WpkR_iYFc2AX zH{{jZonGqhft=|U5dP~Eu$REjsys~%G1zsjGHRyh+-d_1ly#SN-dG|LMTbQAyL?m* zhQd&m;C)TDWqCb#rfA)n9^|MIXyiShZDe`Bw|fv66}2lAZ6fpKDzX}dYlY@#R4T6n zm57{^{mqsS(8kNE!=anzkRUPjda-gx<_)-pr*Wr9ncS%TeaRt^voe8wAzRH=U9aYe zO`q&!L9Ta77V|@AH1bYZ9Td3Pzfrbs3aU5v=O~JWanm9oVka{S)Q}O_tuZ2HcA~9y zHi#4WUIEfb#XM19y0c@g0Fr8aj7F*XM|mJ=!EGq4zlE*k+Id`yV#*edyd_z= z{H+29=q|joxC?!2#;c!dHQ}C$Yti6CNyjWT=>Yr95A8RrAEgkwFOl{s*G8u*O4jqj znyCgm_bv$=Zmvb=fMVPbE^VI4MyTkKfIo?o4<&wCq;{zu8k5~%!Ws7O2I?H-G{@h8p$nbJ+=I& z=^|m{Hmp14PnG(#kTsxNYvEl1eb5oNHp+tJmt`Tr=~CC+40zAr?;5_qzw!O=Gp42Y zSn!P(7CS|Wk^GpMv_j>q8sRK385~7TrsSmlW7(Yr$Y5$AK=rZ+%G&TtSYbFIuJfI+|fO@-vQ+2n4opjyyTq z-9ZYT5~;93{89ZoBDIRgseq7py{_(SrQnE^0x=b$^!L|!lF&UoJ>F3*ArM8e7)0g! ziXt5(In)71)qN!RXp&U?y%fP=6B60kr=2_&=!mBMSc9}MILBD7_w~A-Vksq}?BwY; z#lSu3#HFN{r#;;CF$+M&(r48+k2 zUZHpfj)7Cv;5al!W2Azp*j$ykP;^p{-p;EqhDQU^bj^LI(_Ng;4b4Y6dD{5-RazcK zJ7=#E97jTAV<{mQVL!ZX&pG8hy2CN(TG_chC=B9fX}lTh;!nUpSMMA-C>eb|)JD-8 z(WSq^qmlGO{elFGdxW1d!>N80Pw+aYjof#ss}IZHicXFq8up(6Z+9B@ChQ}GNd)jU zDFFG3i$Vf=)JBY@CUnuwHk9U7auzyy@RsZ+AdK5chA^GoDh?$EnjvsF)kPGNxm}BQ z@~DJOuUQHcJm$P~5lTuf@VF zsOwB&c+6eW!BxUhl9FVPI$+{s=dlj%)PoQ_uHnroq?C{;JXFyOCQVgrc=`IOnr?Q_ zc-s@OkiTEQpxx|Wk(~Kd@W@u+xuKAtgbbhk^J;%_nR{Z=pR<4qJBk^=3{w=G7Pj;qt7zJO zb}Y|5f{mF3Ta&n?Nf`wPO0{=KP4CpONq74F6Oh(_Ppi_u9Q=IvMu8aC1=|iI_H%i$k4s1c<3Z<2zwkiVS z{+|Hu7!l`Ip>jDC-ib{nW`S6Sx|x5#{{ULGJN+CE!^|Ig2A7GrY0gUv2P z8`uZUK@gEU1@BOr>zYP={=rO8Qo*v9?ynUqdveIafu2Npt1?I!{XSF>Tkzo2OWiDS zidaXEJs7(|0ZA%Y5JJL2^7NuJMU{zSH!$*YQ$kHE28(%n(jGv{CIkd?HZ}6o{fGus zutTl9%e?|AH)3cwW3q8eKrEK{&)_DNND*}r2iZuL2uAITbe}CI$VfF%uYN|Dy@CV|A3`FN?dm#3AsQWMlKobIMm(4CHvO*HUjpp;V z)PksDBs$5#CHC=%@1$wmrZX-tLpBA&qSTAy=(f7*i?X3vml z<1VpUnf;#|n!s ziI#jO;X(_8SyJyM3eZC8qwnF;i^nn{6}hum32=t!+0j1DMI{;*J}NjXM7a>AMiS-9 z(E)9i;cmic!U7NkLlaLODgwlO@0KWkC;Q!+cYcyoxQx38E<4iRGZS{5wCznYkkb+- zTLQpYl5sRCOQp<*%7F0`2Jr(-S-{tp4ro$DAjZ`_D|$>OQ@oyYMq_j#JR=l-IN3^+ zMsgp`5YinN!m}}RIfFbHlQSFwFn$xIC1WLRi>c=P1}Hb%ft15t3S_5P*DV-F<=eW zhv+ph6Dvqb$;&M#(GZc5Hj=(57(w90G)t_l7=CXG)WVm5KYBo0Ko~baT9lkLpnO%2 z&OidJ4Dl>uT0}5`W(14T=|D;X$CVmw`wT3Yq|8F`eo;v>QA|MtM!ih5=00Lb4`_E) z;QS_}lv9~w)}CjxW1HEX!P2gPiV`xLnjuWxYGqtLkpdIu#mO@JRl;plp$8e$Mlh@rr*YLd!~!9LV6 zD7g(H4DiV%FatC1Og2=z$!Fq(0#^g|zlY?e^(5Y}t886YL90ln9{FS|eHy2hBH78Ct(vsLm4TI&PizXTXk5f*W zA_K!yklw;p(Do0)r+BLpOEBnx)wNt>b`i8y;$gJvWtlED!&=UvT%dHaC(}@Zm}WR# z?ccS~oJLJ23G6#&uAd97IA(xcM~-PXypwV6TfG$Ck(AMGu%Pu)Hk5#FDPrIq)Dk%) zpjpgE6u=PhH>OECAf<8xLt138=#NYBT}(qSEFP%od(4iDj@9=K`~3!)L}~v3fbj5t zW}n0bBBC)1CJ>Q5Gva_qiO0|xEKs4Us^F(XVH2QKEU{2PFT)EC&)+Hw zm_47y2jStQ1QO+=JWp}MO9i*HW`)+Q{8xrZ>k4x*vNVnY5>VdAoHIy~W~*;Wq!v`f z%E%!{2t))%C_N^DfGD-9Xw5KSpe&U;)N{=u0SHG@i!!nnzjDK(bNr+FU@BMW_KxJj ze5(HdMB9}(NDXTDE)|C5)!_A-gKY8x#fO=BtdSRiowuSW4bOaZ$ zZoUSKMP)3&{9=W16=joP=Lv|bgTCJ_2*8a11|&Tv?Dy544J8Q5S8!gQhenLFHD{nU zOHaH~xKT}}z}S^&y=eac!$(*KQeFi__UPgkZ=5uIkeC5uNuv0Y#Eg}C0;F;`qAHWE z7)mN=Y0Oayx{s!Dn`w&=CW)xEHzX3wc{wnSqCeWOR7I&}`eMXw@=oNH2Y?}{>qGG@ zFzvOb^l4rK0NRWQ8Pb*vH-P@?vwDlY_Y^o+N3wgF-hiC(aw9tkY-_I60!hKJ6Z_Vf zagBwC1W;T$kn-tCgX~OCTu~N8jxgD<*>%>Rgek7@lNQZKBLo4^!f6mJ)Bud#4}=+{ zkTMNjbE5lboJ8V-1nW%;&o5138bh?+Ayi(0jTAkd_nv!@lVR_aTgzUgt0VWGMWi_V6(J2YX2i;PLH0fmN z1?GqU01YLdZ<-@uk0T8LQe!n%4|R!;jRGL|M+!@ru^ITNfk?gPiDDRDI~#V%;e_@Si&CK7Ze4^)FXBt7Du#JZ!Ftw>2FQ%bE8&x+8O=CJma zcZY~1k)pLZ7%V>&MCHr2k9X}(ZCsZ*YT!!NW>QxHjMgH)3QFT-9B|r##3D$V)J#G{ z;H0v*pg=e#^FXB{(~_}6L>gkpwJg1~sf^S(F6(_HBU}$^m_agj8-%mwkLqtnG;d3n zY^UxT>E4eK5E{RBf-1`px=DJ8!}K-KPqHdZWdymiygl(u4naN@DdH(N1~sYXPAe)n za&VNV68oU?dEhhcG_k$#5|gHkaJl&OtJ!6oTz1j7w9WVSO%DYRUhiQ7|$ zieQoV(1Q2fXUq|%=`f#yt`6i2$w|V>if_Vpc>Oh z1QeT0UwU zS>BmbrCoxwryVNhr_D6cb0ruC8XXMuiTR*`GkbqZ#Y19}n2%AUs>j(17g*hZ&%c75 zf-fVXgWtLpcMBbU>*jzEfjF{W9*=eFAs04BaF1D_9zlyC!Rjw+aZH|KmlHAWn?0LS z)RM*&L$vuQL`W2ETcX>vXB3I%uoFT^0@ye{MM_0*#>eEe6LuM)z?|`w00>#VW}|a5 zm3#V+PASxHjo|##R8(muFHC}25^_|40VYMHjm4*jSh;{3G-&5x?uiz{PEJCIGvG46 zOZInTLz;xDB)uYvB-UqYQHov)O5DX8R1!&YMw1GWG!zi6fo!{fMhb$(B7UOMWjDz3 zC{2oYb{<7E(7|SHXSol0&t#;UC^EoxUHcuX@D!-kk@%@AHq|RO zxUsuxIs`%|F;;||V1p%Qq^j6BT z+@GcwVbqXOwGQzFQ;Jwq+WiC`%?i*>4o)efOk>%3zgVaipe&c;^WCA?Ow*-WMUryC z=~Gl;0Gk(RN?=9;HkmQrC=kpdJW>#hv6v8lHFcvVggJ^?cp-SPW;8gRX!3I)xdHcF z1s-S?ZkclA_=Bj@CFYjVLQGf5OpTPqLOA+5(T9qFb*X5M=z-`dOa+(gug_npgXup} zHDmO?z-BjgsK=GlRhl>O{xppjMJFO&CZDGKwJo~0*lbi-y)&C@O)Wq? zLHSf>MPaQzLRC>9FUd=JW&|)hH8&)(th9n*Ol63F4=fTG(O2P!Q-0rUShqU}URFGRFD!MiwG1 zp)NjXWE@Y^2T$TbQ!mg`5u2@q9HZKc+RdNZgt@{4cIajUy&+-FD^{>xh}36CLH1}R z091*{W`f9XSniY63z?J!&cHh-a*x?5@I}XLl2J7#W3%M7GFdKBm1fm~0rD!rhOVtX z$tLAT2LX3KCD~HMlW>0r$=4mr(WJ}5TsiqWcaU8`rISVud=1Wn86$)&1iVTR6!yj*jKtNy| z-1QXV_HyU*SHpHRf0ZUsx|G@vG!0V;$O8y4|TKg>sf1az*PIev~27?v^hUC z13rfogxsH_bWxg^M#avk<}KAI^t2rygIs#3`UD@t)cz5bGfM=tbTNR}K%_+h8&cbj z4=ZX-fPo7Duskk!9qM9E&a2>xA$7x2jU5subv<1I=uBpW04JkX<|N6htZWkYpqlc| zMDIa8s&}Qe5lLLymWR~ILrZG7^{6N89Q&UX1Dea{`27#8N|E|$PcgpiW7JSJ zdQlrewI(O8;Y|ZiB?dfws%K)ozvNM)nL0wn`h))fRMHaHGN41;hrJz@P=E}pL<|%p zE39b1dO@XLtR&%n!BUz;x}+~R?@dz6^e4^E^(0cq50m3lgf$=)7V<=!2C5nRQ3~E9 zP}(*3q+x|1A<{2Tq@mNI8_G*5D&OVVX=toOybD`aW zodyV7y1r?S<^KTU^}dT0l9qnALJCfS_Y!s~K`>^c`|IAi9q~Ruu9eoFt+~yD3K(CT*w*?HEXpYpPEzn9;J_w@S-f@2x?&Xhp9ya za@Ivvo~y}FW+8?dzQXxtY2#9>ag69+1ze1ek6-ular&n{Q^jJh`HzNyNp}^bd=ds% zNIja_-c3fB1?*pT`aCw5fRI3b)d~>AMI}4qCWS+V8t7iEgzTRNk|e$$1G0VSx!@)p zhN$|egh-S@ZnP>#5fR@?8XXODw8ME2TNusQc?sSw8f!?CD4>=BXhWZ%BDK!?alb+> zyVB{z3j5HEvEmo={C#huwP#xWG^7Cx22pW28k80bt2*%G-5|gP3t4YLMc-QR(WzPn zI~2z{S*Vgp7J1U3vJY4?6Ft3AtQfmk#jP^x^+hSrI5|6?G_oi2VRYgLfhtG@0ZqbI zN+eCEN(CT+0oe0PYkLX(swCQ)Ge!Y>&89yTVa`PoH-1{}o#|=Sg7Tc-4!F!qloLSC z!}%#_Mx{b7H=Q6_vqo)2j};bR0%AfM zTU@!Q=8v_^u=lAOjQt!W1p>Q_$si6lX!`(mRUqv{LK{?l{{R=PeFUFKLs9iwv$$40 zv@`(S57MG;?~1V*0O$Vz3P50Ky;xDUuatf2PgMpF5fub3OLR2qd_^P~RyD+s*^=>W zG=pS^w*LSq1-)S)D>%Vj0eZ8$U)!3NGQUdKtm59K)DahBp7k#hE!|!=du>?*;FbuP zZ`OdANI;O>h-Jy%fzbm{SiM35#5=ZWKsRW0kJ#1CoHZLR^%59_j{gArNqo2-kp4!5 zVNxbNa=r2v(KtpWxK z=056ZnY|gOC^uN&KN{3n95RG#<}mJ}qk>QijU(aQwFwL&C|F|@x2H%M{{X;!=?Iu7 z(*zO1MF${!OnfL5A|=WedR#jJIyam7pb=F#;uz_Z=yV7}O&oyT;icDz>Jw8Fm=`zy z0Lok0xY8+FN4fe`A`p*K6cEk4p)~DE1QCTUP$DEqX;B9%GHvXpcYwEC-QIuz1%Uou zx6@ieoD}Z;Wqu(h^8_XB^Fn7Eps$56$cvMLnh#xzMedXI4JcER{5?>{9{MJ3MhGL> z-mqT-Nf`)cC>Cp%3dvB+W=+=Pqs>9Egc4!c$z;irj3mGw5X@@j%|RW}QUb6Js1JsO zIHXfhO2ypMP?4H~!Q6DkBYww14B#?xo+w5rB^CzHxuq-=K2W4-%o@wG-jw$VJ%)Sx wQ)>w+;*l^6A>e+^3~Wk9(;meocIkVE+Lk`CH<)mT+|izsd1KuDHU2;U+4NxqCIA2c literal 0 HcmV?d00001 diff --git a/docs/widget/common/image/index.md b/docs/widget/common/image/index.md index 83532f6..9299e5e 100644 --- a/docs/widget/common/image/index.md +++ b/docs/widget/common/image/index.md @@ -1 +1,51 @@ -## **文档完善中** \ No newline at end of file +## **Image** + +> +用于展示图片的控件,支持本地图片,资源图片,网络图片等加载方式 + +### 构造方法 +``` dart +Image({ + Key key, + @required this.image, + this.semanticLabel, + this.excludeFromSemantics = false, + this.width, + this.height, + this.color, + this.colorBlendMode, + this.fit, + this.alignment = Alignment.center, + this.repeat = ImageRepeat.noRepeat, + this.centerSlice, + this.matchTextDirection = false, + this.gaplessPlayback = false, + this.filterQuality = FilterQuality.low, + }) +``` + +### 属性介绍 +* image: + * Image(): 通用方法,使用ImageProvider(包括:AssetImage, NetworkImage, FileImage, MemoryImage)实现,如下方法本质上也是使用这个方法 + * Image.asset: 加载资源图片 + * Image.file: 加载本地图片文件夹 + * Image.network: 加载网络图片 + * Image.memory: 加载Uint8List资源图片 +* width:图片容器宽度 +* height:图片容器高度 +* color:color一般和colorBlendMod配合使用 +* colorBlendMode:和color配合使用,添加滤镜效果 +* fit:如何将图像分配到布局空间中 + * BoxFit.fill : 全图显示,显示尽可能拉伸,充满 + * BoxFit.contain : 全图显示,显示原比例,不需充满 + * BoxFit.cover : 显示可能拉伸,可能裁剪,充满 + * BoxFit.fitWidth : 显示尽可能拉伸,可能裁剪,宽度充满 + * BoxFit.fitHeight 显示尽可能拉伸,可能裁剪,高度充满: + * BoxFit.none 无fit: + * BoxFit.scaleDown 效果和contain差不多,但此属性不允许显示超过原图片大小,可小不可大: +* alignment = Alignment.center:控制图片的显示位置 +* repeat = ImageRepeat.noRepeat:图片是否重复 +* centerSlice:九个补丁图像的中心切片 +* matchTextDirection = false:若值为turn,与Directionality配合使用,图片显示方向 +* gaplessPlayback = false:当ImageProvider发生变化后,重新加载图片的过程中,原图片的展示是否保留。若值为true,保留,若为false,不保留,直接空白等待下一张图片加载 +* filterQuality = FilterQuality.low:用于设置图像的FilterQuality \ No newline at end of file diff --git a/lib/widget/common/image/demo.dart b/lib/widget/common/image/demo.dart index e073b5c..82b64ff 100644 --- a/lib/widget/common/image/demo.dart +++ b/lib/widget/common/image/demo.dart @@ -6,6 +6,8 @@ class Index extends StatefulWidget { } class _IndexState extends State { + List _imageType = ['Image.network', 'Image.asset', 'ImageProvider']; + @override void initState() { super.initState(); @@ -17,9 +19,60 @@ class _IndexState extends State { appBar: AppBar( title: Text('Image'), ), - body: Center( - child: Text('更新中'), - ), + body: Container( + margin: EdgeInsets.fromLTRB(20.0, 0, 20.0, 0), + child: ListView.builder( + itemCount: _imageType.length, + itemBuilder: (context, index) { + return _Stack(_imageType[index]); + }, + ) + ) ); } + + Widget _Stack (String item) { + return Stack( + children: [ + Container( + margin: EdgeInsets.only(top: 5.0), + width: double.infinity, + child: _Image(item) + ), + Positioned( + top: 32, + left: 32, + child: Text(item, style: TextStyle(fontSize:20, color: Theme.of(context).primaryColor),), + ) + ], + ); + } + + Widget _Image (String item) { + switch (item) { + case 'Image.network': + return Image.network( + 'https://desk-fd.zol-img.com.cn/t_s960x600c5/g2/M00/02/05/Cg-4WlVxbqyIUWdVAAZhyzM-HqkAAEs-AET61oABmHj388.jpg', + fit: BoxFit.cover + ); + break; + case 'Image.asset': + return Image.asset( + 'assets/imgs/cool.jpg', + fit: BoxFit.cover + ); + break; + case 'ImageProvider': + return Image( + image: NetworkImage( + 'https://pic.qqtn.com/up/2017-10/15082099209936659.jpg' + ), + ); + default: + return Image.network( + 'https://desk-fd.zol-img.com.cn/t_s960x600c5/g2/M00/02/05/Cg-4WlVxbqyIUWdVAAZhyzM-HqkAAEs-AET61oABmHj388.jpg', + fit: BoxFit.cover, + ); + } + } } diff --git a/readme/widget_progress.md b/readme/widget_progress.md index 249a773..4e8be2e 100644 --- a/readme/widget_progress.md +++ b/readme/widget_progress.md @@ -55,7 +55,7 @@ Flutter UI │ ├─flatbutton 【✔️ v1.0】 │ ├─icon 【✔️ v1.0】 │ ├─iconbutton - │ ├─image + │ ├─image 【✔️ v1.0】 │ ├─listtile │ ├─placeholder │ ├─raisedbutton From 43bfe2a6bb51044f971f3499e91e3f9a9c1a81d9 Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 3 Apr 2019 16:48:40 +0800 Subject: [PATCH 057/100] feat:ListTile --- docs/widget/common/listtile/index.md | 36 +++++++++++++- lib/widget/common/listtile/demo.dart | 72 ++++++++++++++++++++++++++-- readme/widget_progress.md | 2 +- 3 files changed, 105 insertions(+), 5 deletions(-) diff --git a/docs/widget/common/listtile/index.md b/docs/widget/common/listtile/index.md index 83532f6..d93dcd6 100644 --- a/docs/widget/common/listtile/index.md +++ b/docs/widget/common/listtile/index.md @@ -1 +1,35 @@ -## **文档完善中** \ No newline at end of file +## **ListTile** +> +单个固定高度的行,通常包含一些文本和前导或者尾随的图标,通常在ListView中使用 + +### 构造方法 +``` dart +ListTile({ + Key key, + this.leading, + this.title, + this.subtitle, + this.trailing, + this.isThreeLine = false, + this.dense, + this.contentPadding, + this.enabled = true, + this.onTap, + this.onLongPress, + this.selected = false, + }) +``` + +### 属性介绍 + +* leading:显示左侧的小组件 +* title:标题 +* subtitle:子标题 +* trailing:显示右侧的小组件 +* isThreeLine = false:是否显示三行文本 +* dense:是否垂直密集显示 +* contentPadding:ListTile的内部填充 +* enabled = true:是否是交互式的 +* onTap:点击时的回调 +* onLongPress:长按时的回调 +* selected = false:图标和文本是否以相同颜色呈现 diff --git a/lib/widget/common/listtile/demo.dart b/lib/widget/common/listtile/demo.dart index 23597fa..cd02a26 100644 --- a/lib/widget/common/listtile/demo.dart +++ b/lib/widget/common/listtile/demo.dart @@ -6,6 +6,23 @@ class Index extends StatefulWidget { } class _IndexState extends State { + List data = [ + { + 'enable': true, + 'subtitle': 'subtitle is not focus', + 'selected': false + }, + { + 'enable': false, + 'subtitle': 'subtitle is not focus', + 'selected': true + }, + { + 'enable': true, + 'subtitle': 'subtitle is not focus', + 'selected': true + } + ]; @override void initState() { super.initState(); @@ -17,9 +34,58 @@ class _IndexState extends State { appBar: AppBar( title: Text('ListTile'), ), - body: Center( - child: Text('更新中'), - ), + body: Container( + padding: EdgeInsets.fromLTRB(20.0, 0, 20.0, 0), + child: ListView.builder( + itemCount: data.length, + itemBuilder: (context, index) { + return Column( + children: [ + ListTile( + leading: CircleAvatar( + backgroundImage: AssetImage('assets/imgs/cool.jpg'), + ), + title: Text('title'), + subtitle: Text(data[index]['subtitle']), + trailing: Icon(Icons.arrow_right), + onTap: (){ + if (!data[index]['selected']) { + this.setState((){ + data[index]['subtitle'] = 'subtitle is focus now'; + data[index]['selected'] = true; + }); + } else { + this.setState((){ + data[index]['subtitle'] = 'subtitle is not focus'; + data[index]['selected'] = false; + }); + } + }, + onLongPress: (){ + Scaffold.of(context).showSnackBar( + SnackBar( + content: Text('你长按了我'), + backgroundColor: Theme.of(context).primaryColor, + action: SnackBarAction( + textColor: Colors.white, + label: '取消', + onPressed: (){}, + ), + duration: Duration(seconds: 2), + ) + ); + }, + selected: data[index]['selected'], + isThreeLine: false, + enabled: data[index]['enable'], + contentPadding: EdgeInsets.all(0), + ), + Divider() + ], + ); + }, + ) + ) ); } } diff --git a/readme/widget_progress.md b/readme/widget_progress.md index 4e8be2e..9672697 100644 --- a/readme/widget_progress.md +++ b/readme/widget_progress.md @@ -56,7 +56,7 @@ Flutter UI │ ├─icon 【✔️ v1.0】 │ ├─iconbutton │ ├─image 【✔️ v1.0】 - │ ├─listtile + │ ├─listtile 【✔️ v1.0】 │ ├─placeholder │ ├─raisedbutton │ ├─rawimage From 71f1006ec4c08237d2bb1c6161932468b45c5bb9 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Wed, 3 Apr 2019 17:02:44 +0800 Subject: [PATCH 058/100] =?UTF-8?q?feat=20and=20refactor:=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E9=A1=B9=E7=9B=AE=E7=BB=93=E6=9E=84=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=BC=80=E5=8F=91md=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/imgs/github.png | Bin 0 -> 1288 bytes assets/imgs/github_1.png | Bin 0 -> 2140 bytes assets/imgs/github_2.png | Bin 0 -> 2047 bytes .../{exampleComp.dart => example_comp.dart} | 0 ...expansionTile.dart => expansion_tile.dart} | 0 .../{markdownComp.dart => markdown_comp.dart} | 0 .../{updatingComp.dart => updating_comp.dart} | 0 .../{webviewComp.dart => webview_comp.dart} | 0 .../{widgetComp.dart => widget_comp.dart} | 14 +- lib/controller/index.dart | 3 +- lib/http/index.dart | 46 ++-- lib/http/loading.dart | 103 ++++++--- lib/http/loading2.dart | 64 ------ lib/http/loadingDialog.dart | 119 ----------- lib/http/log.dart | 12 -- lib/page/app-login/index.dart | 31 +++ lib/page/home.dart | 1 + lib/page/mine/index.dart | 2 +- lib/router/handles.dart | 4 +- lib/store/models/user_model.dart | 50 +++-- lib/utils/appVersion.dart | 80 +++---- lib/widget/animate/animatedbuilder/index.dart | 4 +- .../animate/animatedcontainer/index.dart | 4 +- .../animate/animatedcrossfade/index.dart | 4 +- .../animateddefaulttextstyle/index.dart | 4 +- .../animate/animatedliststate/index.dart | 4 +- .../animate/animatedmodalbarrier/index.dart | 4 +- lib/widget/animate/animatedopacity/index.dart | 4 +- .../animate/animatedphysicalmodel/index.dart | 4 +- .../animate/animatedpositioned/index.dart | 4 +- lib/widget/animate/animatedsize/index.dart | 4 +- lib/widget/animate/animatedwidget/index.dart | 4 +- .../animatedwidgetbasestate/index.dart | 4 +- .../animate/animationcontroller/index.dart | 4 +- .../animate/decoratedboxtransition/index.dart | 4 +- lib/widget/animate/fadetransition/index.dart | 4 +- lib/widget/animate/hero/index.dart | 4 +- .../animate/positionedtransition/index.dart | 4 +- .../animate/rotationtransition/index.dart | 4 +- lib/widget/animate/scaletransition/index.dart | 4 +- lib/widget/animate/sizetransition/index.dart | 4 +- lib/widget/animate/slidetransition/index.dart | 4 +- lib/widget/bulletbox/alertdialog/index.dart | 4 +- lib/widget/bulletbox/bottomsheet/index.dart | 4 +- .../bulletbox/expansionPanel/index.dart | 4 +- lib/widget/bulletbox/simpledialog/index.dart | 4 +- lib/widget/bulletbox/snackbar/index.dart | 4 +- lib/widget/common/assetbundle/index.dart | 4 +- lib/widget/common/buttonbar/index.dart | 4 +- lib/widget/common/chip/index.dart | 4 +- lib/widget/common/container/index.dart | 4 +- lib/widget/common/divider/index.dart | 4 +- lib/widget/common/flatbutton/index.dart | 4 +- lib/widget/common/icon/index.dart | 4 +- lib/widget/common/iconbutton/index.dart | 4 +- lib/widget/common/image/index.dart | 4 +- lib/widget/common/listtile/index.dart | 4 +- lib/widget/common/placeholder/index.dart | 4 +- lib/widget/common/raisedbutton/index.dart | 4 +- lib/widget/common/rawimage/index.dart | 4 +- lib/widget/common/stepper/index.dart | 4 +- lib/widget/common/text/index.dart | 4 +- lib/widget/common/tooltip/index.dart | 4 +- lib/widget/form/checkbox/index.dart | 4 +- lib/widget/form/checkboxlisttile/index.dart | 4 +- lib/widget/form/daypicker/index.dart | 4 +- lib/widget/form/form/index.dart | 4 +- lib/widget/form/formfield/index.dart | 4 +- lib/widget/form/radio/index.dart | 4 +- lib/widget/form/radiolisttile/index.dart | 4 +- lib/widget/form/rawkeyboard/index.dart | 4 +- lib/widget/form/slider/index.dart | 4 +- lib/widget/form/switch/index.dart | 4 +- lib/widget/form/switchlisttile/index.dart | 4 +- lib/widget/form/textfield/index.dart | 4 +- lib/widget/form/textinput/index.dart | 4 +- lib/widget/gestures/absorbpointer/index.dart | 4 +- lib/widget/gestures/dismissible/index.dart | 4 +- lib/widget/gestures/dragtarget/index.dart | 4 +- .../gestures/gesturedetector/index.dart | 4 +- lib/widget/gestures/ignorepointer/index.dart | 4 +- .../gestures/longpressdraggable/index.dart | 4 +- lib/widget/navigator/appbar/index.dart | 4 +- .../navigator/bottomnavigationbar/index.dart | 4 +- lib/widget/navigator/drawer/index.dart | 4 +- .../navigator/floatingactionbutton/index.dart | 4 +- lib/widget/navigator/materialapp/index.dart | 4 +- lib/widget/navigator/navigator/index.dart | 4 +- lib/widget/navigator/pageview/index.dart | 4 +- .../navigator/popupmenubutton/index.dart | 4 +- lib/widget/navigator/scaffold/index.dart | 4 +- lib/widget/navigator/tabbar/index.dart | 4 +- lib/widget/navigator/tabbarview/index.dart | 4 +- lib/widget/navigator/widgetsapp/index.dart | 4 +- lib/widget/regular/align/index.dart | 4 +- lib/widget/regular/aspectratio/index.dart | 4 +- lib/widget/regular/center/index.dart | 4 +- lib/widget/regular/column/index.dart | 4 +- lib/widget/regular/constrainedbox/index.dart | 4 +- lib/widget/regular/container/index.dart | 4 +- lib/widget/regular/fittedbox/index.dart | 4 +- lib/widget/regular/flow/index.dart | 4 +- lib/widget/regular/layoutbuilder/index.dart | 4 +- lib/widget/regular/listbody/index.dart | 4 +- lib/widget/regular/listview/index.dart | 4 +- lib/widget/regular/padding/index.dart | 4 +- lib/widget/regular/row/index.dart | 4 +- lib/widget/regular/stack/index.dart | 4 +- lib/widget/regular/table/index.dart | 4 +- lib/widget/regular/wrap/index.dart | 4 +- .../scrollview/customscrollview/index.dart | 4 +- lib/widget/scrollview/gridview/index.dart | 4 +- lib/widget/scrollview/listview/index.dart | 4 +- .../scrollview/nestedscrollview/index.dart | 4 +- lib/widget/scrollview/scrollable/index.dart | 4 +- lib/widget/scrollview/scrollbar/index.dart | 4 +- .../scrollview/scrollcontroller/index.dart | 4 +- .../singlechildscrollview/index.dart | 4 +- lib/widget/vision/backdropfilter/index.dart | 4 +- lib/widget/vision/clipoval/index.dart | 4 +- lib/widget/vision/clippath/index.dart | 4 +- lib/widget/vision/cliprect/index.dart | 4 +- lib/widget/vision/custompaint/index.dart | 4 +- lib/widget/vision/decoratedbox/index.dart | 4 +- .../vision/fractionaltranslation/index.dart | 4 +- lib/widget/vision/mediaquery/index.dart | 4 +- lib/widget/vision/opacity/index.dart | 4 +- lib/widget/vision/rotatedbox/index.dart | 4 +- lib/widget/vision/theme/index.dart | 4 +- lib/widget/vision/transform/index.dart | 4 +- readme/develop.md | 200 ++++++++++++++++++ 131 files changed, 629 insertions(+), 536 deletions(-) create mode 100644 assets/imgs/github.png create mode 100644 assets/imgs/github_1.png create mode 100644 assets/imgs/github_2.png rename lib/components/{exampleComp.dart => example_comp.dart} (100%) rename lib/components/{expansionTile.dart => expansion_tile.dart} (100%) rename lib/components/{markdownComp.dart => markdown_comp.dart} (100%) rename lib/components/{updatingComp.dart => updating_comp.dart} (100%) rename lib/components/{webviewComp.dart => webview_comp.dart} (100%) rename lib/components/{widgetComp.dart => widget_comp.dart} (92%) delete mode 100644 lib/http/loading2.dart delete mode 100644 lib/http/loadingDialog.dart delete mode 100644 lib/http/log.dart create mode 100644 readme/develop.md diff --git a/assets/imgs/github.png b/assets/imgs/github.png new file mode 100644 index 0000000000000000000000000000000000000000..b1ba9a7e7702ba92151bd351e893e277c6eace61 GIT binary patch literal 1288 zcmV+j1^4=iP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1f5AlK~#8N?b=z$ zbyFP2@y9$Bsh1>?Ia7oy$#|iN5Sh7nWy)~FjUq}Rg(yPiq2$Vi+?g)qLXzB==VZ)0 zPv5VeUAxu)JZGQtti9JhtY3ZVdC$AgUjMz%9@kn+H8nLgH8nLgH8o?&y4V+&;VwLj zxA8T8UFP3@=Ks0OrSLoJ;6E96%3-)4U*hjMeF4{TC|u{lXCqvQcMxsxYPik|U>ge; zPldDb38Ib5>0_LQg*p9p#q)?Zrccjd7mPY*;5$ScFobV$GDa5<@0$=6UBdMk%_+A& z9zs-TN#5<&L(%vvJ%XsfF?bk7kE9zA6*eZGZbfn$qJqZg6bu?~xz;k-qbI!SF$BU&8b8jJ9eip9dOr#g!^ARoIcm{)9 zk9O@WR0g+*Pg(~Q7TU$swwq3vYux42S*L1 zeuQ_EQrQ+iA*wAk=}}~^*l8X$lDZY82gW<4cYPyqDI>MMT++SF2{Z%H$JMd&27q-E*CwG zVc>3eM75Rx2a#fTb7c`@G+rQ#{x z<%S|>on*^_h#HAJic;zNHn07WEp~T&hNz*4*MXtq5fRlAaa*!az;diRYAo^xPQ*}g z5YslJS|VOkv*!pzjZN|=Zoww#E1SXYjlU4pFp1sA*)yrr{%PEd7h9jT`BZy3wnVOM zjVs|WPSoZ~dh4_|7a?u%3N$$yc4wx`$oDzBO|Hgq*bQ9@bs2mAPQrEYhv<5ziZ}w> zTRdHy6PPyYGetAv>Be+9mp9K6a384t^`2 zHy+*|O&=iIn4CMvHf(E@r_{7K>C*?iH__fGyG~|JIw`?*I%m>07C!#Ehrrj5t1!>q y#qZb?;5v@PLLMDqwmgQjNy00005?>x6Ipn?1 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 TPqFEC{% { this._bodyList.add(Divider()); } if (mdText.length > 30) { - this._bodyList.add(await MarkDownComp.Index(mdText)); + this._bodyList.add(await markdown_comp.Index(mdText)); // demo if (widget.demoChild != null && widget.demoChild.length > 0) { widget.demoChild.forEach((Widget item) { - this._bodyList.add(ExampleComp.Index(child: item)); + this._bodyList.add(example_comp.Index(child: item)); }); } } else { - this._bodyList.add(UpdatingComp.Index()); + this._bodyList.add(updating_comp.Index()); } setState(() { this.loading = false; @@ -97,7 +97,7 @@ class IndexState extends State { @override Widget build(BuildContext context) { - print('widgetcomp context =$context'); + print('widget_comp context =$context'); return Scaffold( appBar: AppBar( elevation: 0, diff --git a/lib/controller/index.dart b/lib/controller/index.dart index 02deea6..be71ebb 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -6,8 +6,9 @@ import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; void initState() { // 获取版本号 Store.valueNotCtx().$getAppVersion(); + // Store.valueNotCtx().$getUserInfo(); // 登录 - Store.valueNotCtx().$getUserInfo(); + Store.valueNotCtx().$getLocalUserInfo(); Future.delayed(Duration(seconds: 3), () { AppVersion().check(Store.widgetCtx); }); diff --git a/lib/http/index.dart b/lib/http/index.dart index aa104f7..cb27f28 100644 --- a/lib/http/index.dart +++ b/lib/http/index.dart @@ -8,9 +8,7 @@ import 'package:dio/dio.dart' LogInterceptor, Response; import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; -import 'log.dart' show log; -import 'package:efox_flutter/store/index.dart' show Store; -import 'loadingDialog.dart' as AppLoading; +import 'loading.dart' as AppLoading; Dio getDio({options, loading}) { if (options == null) { @@ -35,21 +33,21 @@ Dio getDio({options, loading}) { options.headers['Authorization'] = 'token $token'; } await AppLoading.beforeRequest(options.uri, loading); - log('【发送请求】 ${options.uri} ', '${options.headers} ${options.data}'); - // Do something before request is sent - return options; //continue - // If you want to resolve the request with some custom data, - // you can return a `Response` object or return `dio.resolve(data)`. - // If you want to reject the request with a error message, - // you can return a `DioError` object or return `dio.reject(errMsg)` + print('=========【发送请求】Start============'); + print("请求地址 ${options.uri}"); + print("请求头 ${options.headers}"); + print("请求参数 ${options.data}"); + print('=========【发送请求】End============'); + return options; }, onResponse: (Response response) async { - log('【请求成功】 ${response.request.uri},【状态码 ${response.statusCode}】', - response); - return Future.delayed(Duration(seconds: 3), () async { - await AppLoading.afterResponse(response.request.uri, loading); - return {'data': response.data}; // continue - }); + print('=========【请求成功】Start============'); + print("请求地址 ${response.request.uri}"); + print("请求头 ${response.statusCode}"); + print("请求参数 ${response.data}"); + print('=========【请求成功】End============'); + await AppLoading.afterResponse(response.request.uri, loading); + return response; }, onError: (DioError e) async { await AppLoading.afterResponse(e.request.uri, loading); @@ -61,8 +59,12 @@ Dio getDio({options, loading}) { msg = e.response.data; status = e.response.statusCode; } - log('【请求失败】 ${e.request.uri},【状态码 ${status}】【code】: ${code}', msg); - return {'msg': msg, 'code': code, 'status': status}; + print('========【请求失败 Start】============='); + print("请求地址 ${e.request.uri}"); + print("状态码 ${status}"); + print("返回msg ${msg}"); + print('=========【请求失败 End】============'); + return dio.reject({'msg': msg, 'code': code, 'status': status}); }, )); dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志 @@ -72,14 +74,10 @@ Dio getDio({options, loading}) { Future get({url, data = const {}, options, loading}) async { return getDio(options: options, loading: loading ?? Map()) - .get(url) - .then((resp) => resp.data); + .get(url); } Future post({url, data = const {}, options, loading}) async { return getDio(options: options, loading: loading ?? Map()) - .post(url, data: data) - .then((resp) { - return resp.data; - }); + .post(url, data: data); } diff --git a/lib/http/loading.dart b/lib/http/loading.dart index a6874e0..87ea73b 100644 --- a/lib/http/loading.dart +++ b/lib/http/loading.dart @@ -1,43 +1,86 @@ import 'package:flutter/material.dart'; +import 'package:efox_flutter/store/index.dart' show Store; -// ignore: must_be_immutable -class NetLoadingDialog extends StatefulWidget { - String loadingText; - bool outsideDismiss; +bool loading = false; +Set dict = Set(); - Function dismissDialog; +void beforeRequest(uri, Map options) { + dict.add(uri); + if (loading == false) { + showAppLoading(options); + loading = true; + } +} - NetLoadingDialog( - {Key key, - this.loadingText = "loading...", - this.outsideDismiss = true, - this.dismissDialog}) - : super(key: key); +void afterResponse(uri, Map options) { + dict.remove(uri); + if (dict.length == 0 && loading == true) { + Navigator.of(Store.widgetCtx, rootNavigator: true).pop('close dialog'); + loading = false; + } +} - @override - State createState() => _LoadingDialog(); +/** + * loading: 可配置参数 + * requestOrComplete: 是否发送请求或已完成 true表示发送请求需要开启loading,false表示完成请求可关闭loading + */ +void showAppLoading(Map options) { + options = { + 'notLoading': options['notLoading'] ?? false, + 'text': options['text'] ?? 'loading...' + }; + showDialog( + context: Store.widgetCtx, + builder: (context) { + return LoadingDialog(text: options['text']); + }, + ); } -class _LoadingDialog extends State { - _dismissDialog() { - Navigator.of(context).pop(); - } +class LoadingDialog extends StatefulWidget { + final String text; + LoadingDialog({Key key, @required this.text}) : super(key: key); @override - void initState() { - super.initState(); - if (widget.dismissDialog != null) { - widget.dismissDialog( - - //将关闭 dialog的方法传递到调用的页面. - (){Navigator.of(context).pop();} + LoadingDialogState createState() => LoadingDialogState(); +} - ); - } +class LoadingDialogState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + child: CircularProgressIndicator( + strokeWidth: 5, + ), + height: 50, + width: 50, + ), + Divider( + height: 10, + ), + widget.text != null + ? Text( + widget.text, + style: TextStyle( + color: Theme.of(context).primaryTextTheme.title.color), + ) + : '' + ], + ), + ), + ); } @override - Widget build(BuildContext context) { - return Text('haha'); - } -} \ No newline at end of file + void dispose() { + super.dispose(); + loading = false; + dict.clear(); + } +} diff --git a/lib/http/loading2.dart b/lib/http/loading2.dart deleted file mode 100644 index 32985fa..0000000 --- a/lib/http/loading2.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:efox_flutter/store/index.dart' show Store; - -OverlayEntry overlayEntry = null; -var count = 0; -/** - * loading: 可配置参数 - * requestOrComplete: 是否发送请求或已完成 true表示发送请求需要开启loading,false表示完成请求可关闭loading - */ -void showAppLoading(loading, requestOrComplete) { - // showDialog( - // context: Store.widgetCtx, - // builder: (context) { - // return Center( - // child: CircularProgressIndicator( - // // value: 0.5, - // semanticsLabel: 'hsh', - // semanticsValue: 'sds', - // ), - // ); - // }); - if (requestOrComplete) { - ++count; - } else { - --count; - } - if (count >= 1 && overlayEntry?.maintainState == true) return; - if (overlayEntry?.maintainState == true && - requestOrComplete == false && - count == 0) { - print('移除啦~~~'); - overlayEntry.maintainState = false; - overlayEntry.remove(); - return; - } - // var overlayState = Overlay.of(Store.widgetCtx); - - Navigator.of(Store.widgetCtx).push(MaterialPageRoute(builder: (context) { - return Scaffold( - backgroundColor: Colors.transparent, - // body: OverlayEntry(), - ); - })); - overlayEntry.maintainState = true; - - // Navigator.of(Store.widgetCtx).push(MaterialPageRoute(builder: (context) { - // return Index(); - // })); -} - -// class Index extends Dialog { -// @override -// Widget build(BuildContext context) { -// final ThemeData theme = Theme.of(context, shadowThemeOnly: true); -// return Scaffold( -// backgroundColor: Colors.transparent, -// body: Center( -// child: Column( -// children: [CircularProgressIndicator(), Text('loading...')], -// ), -// ), -// ); -// } -// } diff --git a/lib/http/loadingDialog.dart b/lib/http/loadingDialog.dart deleted file mode 100644 index f172698..0000000 --- a/lib/http/loadingDialog.dart +++ /dev/null @@ -1,119 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:efox_flutter/store/index.dart' show Store; - -bool loading = false; -Set dict = Set(); - -void beforeRequest(uri, Map options) { - dict.add(uri); - if (loading == false) { - showAppLoading(options); - loading = true; - } -} - -void afterResponse(uri, Map options) { - dict.remove(uri); - if (dict.length == 0 && loading == true) { - Navigator.of(Store.widgetCtx, rootNavigator: true).pop('close dialog'); - loading = false; - } -} - -/** - * loading: 可配置参数 - * requestOrComplete: 是否发送请求或已完成 true表示发送请求需要开启loading,false表示完成请求可关闭loading - */ -void showAppLoading(Map options) { - options = { - 'notLoading': options['notLoading'] ?? false, - 'text': options['text'] ?? 'loading...' - }; - showDialog( - context: Store.widgetCtx, - builder: (context) { - return LoadingDialog(text: options['text']); - }, - ); -} - -class LoadingDialog extends StatefulWidget { - final String text; - LoadingDialog({Key key, @required this.text}) : super(key: key); - - @override - LoadingDialogState createState() => LoadingDialogState(); -} - -class LoadingDialogState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.transparent, - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - child: CircularProgressIndicator( - strokeWidth: 5, - ), - height: 50, - width: 50, - ), - Divider( - height: 10, - ), - widget.text != null - ? Text( - widget.text, - style: TextStyle( - color: Theme.of(context).primaryTextTheme.title.color), - ) - : '' - ], - ), - ), - ); - } - - @override - void dispose() { - super.dispose(); - loading = false; - dict.clear(); - } -} - -// var overlayState = Overlay.of(Store.widgetCtx); -// overlayEntry = new OverlayEntry(builder: (context) { -// return Scaffold( -// backgroundColor: Colors.transparent, -// body: SizedBox.expand( -// child: Container( -// decoration: BoxDecoration( -// color: Color.fromARGB(100, 255, 255, 255), -// ), -// child: GestureDetector( -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.center, -// mainAxisAlignment: MainAxisAlignment.center, -// children: [ -// CircularProgressIndicator(), -// Text('loading...') -// ], -// ), -// onTap: () { -// print('移除啦~~~ ${overlayEntry.maintainState}'); -// overlayEntry?.remove(); -// loading = false; -// print('overlayEntry ${overlayEntry}'); -// }, -// ), -// ), -// ), -// ); -// }); -// 加载框 -// overlayState.insert(overlayEntry); -// overlayEntry.maintainState = true; diff --git a/lib/http/log.dart b/lib/http/log.dart deleted file mode 100644 index b695293..0000000 --- a/lib/http/log.dart +++ /dev/null @@ -1,12 +0,0 @@ -/** - * 日志打印 - */ -void log(title, message) { - if (message == null) { - print('$title'); - } else { - print('$title'); - print('$message'); - print('end'); - }; -} diff --git a/lib/page/app-login/index.dart b/lib/page/app-login/index.dart index de1a1a7..9c261da 100644 --- a/lib/page/app-login/index.dart +++ b/lib/page/app-login/index.dart @@ -86,6 +86,37 @@ class _IndexState extends State { ) ], ), + ), + SizedBox( + height: 10, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + child: Text( + 'Github账户登录说明', + style: TextStyle( + decoration: TextDecoration.underline, + textBaseline: TextBaseline.ideographic, + decorationColor: Color(0xff000000), + ), + ), + ), + SizedBox( + width: 10, + ), + GestureDetector( + child: Text( + '软件许可及服务协议', + style: TextStyle( + decoration: TextDecoration.underline, + textBaseline: TextBaseline.ideographic, + decorationColor: const Color(0xff000000), + ), + ), + ), + ], ) ], ), diff --git a/lib/page/home.dart b/lib/page/home.dart index 4939d3f..f807b03 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -102,6 +102,7 @@ class _IndexState extends State { title: Text(AppLocalizations.$t('common.logout')), onTap: () { Store.value(context).$clearUserInfo(); + // Store.value(context).$getUserInfo(); }, ), ], diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index e129aa7..cc14bf4 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -5,7 +5,7 @@ import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'package:efox_flutter/store/index.dart' show ConfigModel, Store; import 'package:efox_flutter/config/color.dart' show materialColor; import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; -import 'package:efox_flutter/components/expansionTile.dart' as Comp; +import 'package:efox_flutter/components/expansion_tile.dart' as Comp; class _IndexState extends State { @override diff --git a/lib/router/handles.dart b/lib/router/handles.dart index 53a1878..201bae9 100644 --- a/lib/router/handles.dart +++ b/lib/router/handles.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:fluro/fluro.dart'; -import 'package:efox_flutter/components/webviewComp.dart' as WebViewComp; +import 'package:efox_flutter/components/webview_comp.dart' as webview_comp; Handler webviewHandler = Handler( handlerFunc: (BuildContext context, Map params) { String url = params["url"]?.first; String title = params["title"]?.first ?? 'WebView'; - return WebViewComp.Index( + return webview_comp.Index( url: url, title: title ); diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index d77ec28..6d78224 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -15,12 +15,8 @@ class UserModel with ChangeNotifier { Future $loginController(context, payload) async { dynamic result = await $login(payload); if (result == true) { - Scaffold.of(context).showSnackBar(new SnackBar( - content: new Text('登录成功'), - )); Navigator.of(context).pop(); } else { - print('response $result'); Scaffold.of(context).showSnackBar(new SnackBar( content: new Text('登录失败'), )); @@ -34,45 +30,60 @@ class UserModel with ChangeNotifier { var credentials = base64.encode(bytes); const data = { "scopes": ["user", "repo", "gist", "notifications", "public_repo"], - "note": "admin_script2", + "note": "admin_script", "client_id": "d8eef6133f1a2be3a842", "client_secret": "2b005eed01c72aefd68fac5c5c7f2654f81c227a" }; Options options = Options(headers: {'Authorization': 'Basic $credentials'}); - var response = await Http.post( + var response = Http.post( url: 'https://api.github.com/authorizations', data: data, options: options, ); - if (response['data'] != null) { - $setLoginRespInfo(response['data']); + return await response.then((resp) async { + await $setLoginRespInfo(resp.data); return true; - } else { - // $clearUserInfo(); - return response; - } + }).catchError((error) { + $clearUserInfo(); + return false; + }); } - $setLoginRespInfo(payload) { + $setLoginRespInfo(payload) async { GitHubRespInfo user = GitHubRespInfo.fromJson(payload); LocalStorage.set('githubRespInfo', user.toString()); + print('user.token.toString() ${user.token.toString()}'); LocalStorage.set('githubRespLoginToken', user.token.toString()); - $getUserInfo(); // 授权成功获取用户信息 + await $getUserInfo(); // 授权成功获取用户信息 } /** * 授权成功或打开app时获取用户信息 */ Future $getUserInfo() async { - var response = await Http.post( + var response = Http.post( url: 'https://api.github.com/user', ); - if (response['data'] != null) { - UserInfo user = UserInfo.fromJson(response['data']); + await response.then((resp) { + UserInfo user = UserInfo.fromJson(resp.data); $setUserInfo(user); - } else { - $clearUserInfo(); + }).catchError((error) { + print('ERROR $error'); + // $clearUserInfo(); + }); + } + + /** + * 获取本地数据,减少调用接口 + */ + $getLocalUserInfo() async { + String data = await LocalStorage.get('githubUserInfo'); + if (data == null) { + $getUserInfo(); + return; } + UserInfo user = UserInfo.fromJson(data); + $setUserInfo(user); } /** @@ -80,6 +91,7 @@ class UserModel with ChangeNotifier { */ $setUserInfo(payload) { user = payload; + LocalStorage.set('githubUserInfo', json.encode(payload)); notifyListeners(); } diff --git a/lib/utils/appVersion.dart b/lib/utils/appVersion.dart index d8fb513..6a894b7 100644 --- a/lib/utils/appVersion.dart +++ b/lib/utils/appVersion.dart @@ -42,7 +42,6 @@ class AppVersion { String platform = Platform.isAndroid ? 'android' : 'ios'; print('version=$version $platform'); Map d = await checkVersion(version, platform); - print(d); if (d['isNew']) { this._showDialog(context, d); } else if (showTips) { @@ -52,53 +51,29 @@ class AppVersion { } } - void _showDialog(context, d) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text('升级提示'), - content: Text('发现新版本 ${d['version']}'), - actions: [ - FlatButton( - child: Text('取消'), - onPressed: () { - Navigator.pop(context); - }, - ), - FlatButton( - textColor: Theme.of(context).primaryColor, - child: Text('确定'), - onPressed: () async { - await _downAndInstall(d['version']); - Navigator.pop(context); - }, - ) - ], - ); - }); - } - Future checkVersion(String version, String platform) async { Map d = { 'version': version, 'isNew': false, 'platform': platform }; - var res = await Http.get( + var response = Http.get( url: 'https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/version.json', ); - if (res['data'] != null) { - res = json.decode(res['data']); - print('res=${res['version']}'); - String newVersion = (res['version'] != null) ? res['version'] : version; - //newVersion = '1.0.1'; //debug code - print('$newVersion $res $version'); - d['isNew'] = (newVersion == version) ? false : true; - d['version'] = newVersion; - return Future.value(d); - } + return await response.then((resp) { + if (resp.data != null) { + var data = json.decode(resp.data); + String newVersion = + (data['version'] != null) ? data['version'] : version; + print('$newVersion $data $version'); + d['isNew'] = (newVersion == version) ? false : true; + d['version'] = newVersion; + return Future.value(d); + } + }).catchError((error) { + print('error $error'); + }); } Future _downAndInstall(String version) async { @@ -124,4 +99,31 @@ class AppVersion { } }); } + + void _showDialog(context, d) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('升级提示'), + content: Text('发现新版本 ${d['version']}'), + actions: [ + FlatButton( + child: Text('取消'), + onPressed: () { + Navigator.pop(context); + }, + ), + FlatButton( + textColor: Theme.of(context).primaryColor, + child: Text('确定'), + onPressed: () async { + await _downAndInstall(d['version']); + Navigator.pop(context); + }, + ) + ], + ); + }); + } } diff --git a/lib/widget/animate/animatedbuilder/index.dart b/lib/widget/animate/animatedbuilder/index.dart index 1f4c246..da4da47 100644 --- a/lib/widget/animate/animatedbuilder/index.dart +++ b/lib/widget/animate/animatedbuilder/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animatedcontainer/index.dart b/lib/widget/animate/animatedcontainer/index.dart index 3ca3f54..e95997d 100644 --- a/lib/widget/animate/animatedcontainer/index.dart +++ b/lib/widget/animate/animatedcontainer/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animatedcrossfade/index.dart b/lib/widget/animate/animatedcrossfade/index.dart index 2c2041a..a19c0d8 100644 --- a/lib/widget/animate/animatedcrossfade/index.dart +++ b/lib/widget/animate/animatedcrossfade/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animateddefaulttextstyle/index.dart b/lib/widget/animate/animateddefaulttextstyle/index.dart index 8f31477..f2b7620 100644 --- a/lib/widget/animate/animateddefaulttextstyle/index.dart +++ b/lib/widget/animate/animateddefaulttextstyle/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animatedliststate/index.dart b/lib/widget/animate/animatedliststate/index.dart index 6096f2b..d8eaeed 100644 --- a/lib/widget/animate/animatedliststate/index.dart +++ b/lib/widget/animate/animatedliststate/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animatedmodalbarrier/index.dart b/lib/widget/animate/animatedmodalbarrier/index.dart index 310a73f..5860446 100644 --- a/lib/widget/animate/animatedmodalbarrier/index.dart +++ b/lib/widget/animate/animatedmodalbarrier/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animatedopacity/index.dart b/lib/widget/animate/animatedopacity/index.dart index dda9d7b..31726f6 100644 --- a/lib/widget/animate/animatedopacity/index.dart +++ b/lib/widget/animate/animatedopacity/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animatedphysicalmodel/index.dart b/lib/widget/animate/animatedphysicalmodel/index.dart index 08ef989..db8606b 100644 --- a/lib/widget/animate/animatedphysicalmodel/index.dart +++ b/lib/widget/animate/animatedphysicalmodel/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animatedpositioned/index.dart b/lib/widget/animate/animatedpositioned/index.dart index 4951576..13e25d7 100644 --- a/lib/widget/animate/animatedpositioned/index.dart +++ b/lib/widget/animate/animatedpositioned/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animatedsize/index.dart b/lib/widget/animate/animatedsize/index.dart index 9e3dd0f..ee7c7d1 100644 --- a/lib/widget/animate/animatedsize/index.dart +++ b/lib/widget/animate/animatedsize/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animatedwidget/index.dart b/lib/widget/animate/animatedwidget/index.dart index da17cd6..92a6b89 100644 --- a/lib/widget/animate/animatedwidget/index.dart +++ b/lib/widget/animate/animatedwidget/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animatedwidgetbasestate/index.dart b/lib/widget/animate/animatedwidgetbasestate/index.dart index ac7338b..72d2226 100644 --- a/lib/widget/animate/animatedwidgetbasestate/index.dart +++ b/lib/widget/animate/animatedwidgetbasestate/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/animationcontroller/index.dart b/lib/widget/animate/animationcontroller/index.dart index 707c03d..a19d1f2 100644 --- a/lib/widget/animate/animationcontroller/index.dart +++ b/lib/widget/animate/animationcontroller/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/decoratedboxtransition/index.dart b/lib/widget/animate/decoratedboxtransition/index.dart index c699113..1a96f6a 100644 --- a/lib/widget/animate/decoratedboxtransition/index.dart +++ b/lib/widget/animate/decoratedboxtransition/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/fadetransition/index.dart b/lib/widget/animate/fadetransition/index.dart index 5ce625f..1eacdf9 100644 --- a/lib/widget/animate/fadetransition/index.dart +++ b/lib/widget/animate/fadetransition/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/hero/index.dart b/lib/widget/animate/hero/index.dart index 4c597b0..bdc0dce 100644 --- a/lib/widget/animate/hero/index.dart +++ b/lib/widget/animate/hero/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/positionedtransition/index.dart b/lib/widget/animate/positionedtransition/index.dart index 7640c1d..159155a 100644 --- a/lib/widget/animate/positionedtransition/index.dart +++ b/lib/widget/animate/positionedtransition/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/rotationtransition/index.dart b/lib/widget/animate/rotationtransition/index.dart index 485a970..e2ee19e 100644 --- a/lib/widget/animate/rotationtransition/index.dart +++ b/lib/widget/animate/rotationtransition/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/scaletransition/index.dart b/lib/widget/animate/scaletransition/index.dart index a974852..c98a659 100644 --- a/lib/widget/animate/scaletransition/index.dart +++ b/lib/widget/animate/scaletransition/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/sizetransition/index.dart b/lib/widget/animate/sizetransition/index.dart index 9850ae1..f75c8a9 100644 --- a/lib/widget/animate/sizetransition/index.dart +++ b/lib/widget/animate/sizetransition/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/animate/slidetransition/index.dart b/lib/widget/animate/slidetransition/index.dart index 6d8b4de..762116e 100644 --- a/lib/widget/animate/slidetransition/index.dart +++ b/lib/widget/animate/slidetransition/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/bulletbox/alertdialog/index.dart b/lib/widget/bulletbox/alertdialog/index.dart index ae86f1c..0c116af 100644 --- a/lib/widget/bulletbox/alertdialog/index.dart +++ b/lib/widget/bulletbox/alertdialog/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/bulletbox/bottomsheet/index.dart b/lib/widget/bulletbox/bottomsheet/index.dart index ef41cf2..51faffd 100644 --- a/lib/widget/bulletbox/bottomsheet/index.dart +++ b/lib/widget/bulletbox/bottomsheet/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/bulletbox/expansionPanel/index.dart b/lib/widget/bulletbox/expansionPanel/index.dart index 4b217d6..6290217 100644 --- a/lib/widget/bulletbox/expansionPanel/index.dart +++ b/lib/widget/bulletbox/expansionPanel/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/bulletbox/simpledialog/index.dart b/lib/widget/bulletbox/simpledialog/index.dart index 79c1146..f8e56fb 100644 --- a/lib/widget/bulletbox/simpledialog/index.dart +++ b/lib/widget/bulletbox/simpledialog/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/bulletbox/snackbar/index.dart b/lib/widget/bulletbox/snackbar/index.dart index ad21f89..b84b7e6 100644 --- a/lib/widget/bulletbox/snackbar/index.dart +++ b/lib/widget/bulletbox/snackbar/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/assetbundle/index.dart b/lib/widget/common/assetbundle/index.dart index 1da5006..7da1825 100644 --- a/lib/widget/common/assetbundle/index.dart +++ b/lib/widget/common/assetbundle/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/buttonbar/index.dart b/lib/widget/common/buttonbar/index.dart index 8b0e2e5..df954e3 100644 --- a/lib/widget/common/buttonbar/index.dart +++ b/lib/widget/common/buttonbar/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/chip/index.dart b/lib/widget/common/chip/index.dart index 1977355..751062c 100644 --- a/lib/widget/common/chip/index.dart +++ b/lib/widget/common/chip/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/container/index.dart b/lib/widget/common/container/index.dart index 756d234..94726a7 100644 --- a/lib/widget/common/container/index.dart +++ b/lib/widget/common/container/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'package:efox_flutter/widget/regular/container/demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/divider/index.dart b/lib/widget/common/divider/index.dart index 181c3f1..eeacfee 100644 --- a/lib/widget/common/divider/index.dart +++ b/lib/widget/common/divider/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/flatbutton/index.dart b/lib/widget/common/flatbutton/index.dart index 4980ac8..566c4b0 100644 --- a/lib/widget/common/flatbutton/index.dart +++ b/lib/widget/common/flatbutton/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/icon/index.dart b/lib/widget/common/icon/index.dart index 1a4ae10..8a31f2c 100644 --- a/lib/widget/common/icon/index.dart +++ b/lib/widget/common/icon/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/iconbutton/index.dart b/lib/widget/common/iconbutton/index.dart index 64f6e2e..316a49b 100644 --- a/lib/widget/common/iconbutton/index.dart +++ b/lib/widget/common/iconbutton/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/image/index.dart b/lib/widget/common/image/index.dart index 7a1dab6..9c35166 100644 --- a/lib/widget/common/image/index.dart +++ b/lib/widget/common/image/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/listtile/index.dart b/lib/widget/common/listtile/index.dart index bc86ee7..374eb35 100644 --- a/lib/widget/common/listtile/index.dart +++ b/lib/widget/common/listtile/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/placeholder/index.dart b/lib/widget/common/placeholder/index.dart index f28b6e3..6bc5cd3 100644 --- a/lib/widget/common/placeholder/index.dart +++ b/lib/widget/common/placeholder/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/raisedbutton/index.dart b/lib/widget/common/raisedbutton/index.dart index 4053205..7c4c8d8 100644 --- a/lib/widget/common/raisedbutton/index.dart +++ b/lib/widget/common/raisedbutton/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/rawimage/index.dart b/lib/widget/common/rawimage/index.dart index 2233c11..15e2811 100644 --- a/lib/widget/common/rawimage/index.dart +++ b/lib/widget/common/rawimage/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/stepper/index.dart b/lib/widget/common/stepper/index.dart index 0da390a..6aea1bc 100644 --- a/lib/widget/common/stepper/index.dart +++ b/lib/widget/common/stepper/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/text/index.dart b/lib/widget/common/text/index.dart index 895ef2a..8e6aa49 100644 --- a/lib/widget/common/text/index.dart +++ b/lib/widget/common/text/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/common/tooltip/index.dart b/lib/widget/common/tooltip/index.dart index c0135be..6b61542 100644 --- a/lib/widget/common/tooltip/index.dart +++ b/lib/widget/common/tooltip/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/checkbox/index.dart b/lib/widget/form/checkbox/index.dart index 1bd002c..01e6a07 100644 --- a/lib/widget/form/checkbox/index.dart +++ b/lib/widget/form/checkbox/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/checkboxlisttile/index.dart b/lib/widget/form/checkboxlisttile/index.dart index 46bfbf1..939bb34 100644 --- a/lib/widget/form/checkboxlisttile/index.dart +++ b/lib/widget/form/checkboxlisttile/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/daypicker/index.dart b/lib/widget/form/daypicker/index.dart index 4004052..96cf611 100644 --- a/lib/widget/form/daypicker/index.dart +++ b/lib/widget/form/daypicker/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/form/index.dart b/lib/widget/form/form/index.dart index 9ceb33c..f480dc4 100644 --- a/lib/widget/form/form/index.dart +++ b/lib/widget/form/form/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/formfield/index.dart b/lib/widget/form/formfield/index.dart index 9eeb293..94e6c86 100644 --- a/lib/widget/form/formfield/index.dart +++ b/lib/widget/form/formfield/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/radio/index.dart b/lib/widget/form/radio/index.dart index 3d82317..ee72380 100644 --- a/lib/widget/form/radio/index.dart +++ b/lib/widget/form/radio/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/radiolisttile/index.dart b/lib/widget/form/radiolisttile/index.dart index 98069b1..58a5f32 100644 --- a/lib/widget/form/radiolisttile/index.dart +++ b/lib/widget/form/radiolisttile/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/rawkeyboard/index.dart b/lib/widget/form/rawkeyboard/index.dart index 9ec6dfa..267a260 100644 --- a/lib/widget/form/rawkeyboard/index.dart +++ b/lib/widget/form/rawkeyboard/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/slider/index.dart b/lib/widget/form/slider/index.dart index 7154e8d..cd0f20c 100644 --- a/lib/widget/form/slider/index.dart +++ b/lib/widget/form/slider/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/switch/index.dart b/lib/widget/form/switch/index.dart index 360663b..4180935 100644 --- a/lib/widget/form/switch/index.dart +++ b/lib/widget/form/switch/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/switchlisttile/index.dart b/lib/widget/form/switchlisttile/index.dart index 5d6a06a..cd06331 100644 --- a/lib/widget/form/switchlisttile/index.dart +++ b/lib/widget/form/switchlisttile/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/textfield/index.dart b/lib/widget/form/textfield/index.dart index 0f7b798..7a78e07 100644 --- a/lib/widget/form/textfield/index.dart +++ b/lib/widget/form/textfield/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/form/textinput/index.dart b/lib/widget/form/textinput/index.dart index f50c6c3..d0542ea 100644 --- a/lib/widget/form/textinput/index.dart +++ b/lib/widget/form/textinput/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/gestures/absorbpointer/index.dart b/lib/widget/gestures/absorbpointer/index.dart index 0a18f97..7eedf9a 100644 --- a/lib/widget/gestures/absorbpointer/index.dart +++ b/lib/widget/gestures/absorbpointer/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/gestures/dismissible/index.dart b/lib/widget/gestures/dismissible/index.dart index a000086..e1556c0 100644 --- a/lib/widget/gestures/dismissible/index.dart +++ b/lib/widget/gestures/dismissible/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/gestures/dragtarget/index.dart b/lib/widget/gestures/dragtarget/index.dart index e5b5805..459b08a 100644 --- a/lib/widget/gestures/dragtarget/index.dart +++ b/lib/widget/gestures/dragtarget/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/gestures/gesturedetector/index.dart b/lib/widget/gestures/gesturedetector/index.dart index 7e27b43..cefbe16 100644 --- a/lib/widget/gestures/gesturedetector/index.dart +++ b/lib/widget/gestures/gesturedetector/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; import 'demo_tap.dart' as DemoTap; import 'demo_pan.dart' as DemoPanDrag; @@ -18,7 +18,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/gestures/ignorepointer/index.dart b/lib/widget/gestures/ignorepointer/index.dart index 7d66ba4..afdc871 100644 --- a/lib/widget/gestures/ignorepointer/index.dart +++ b/lib/widget/gestures/ignorepointer/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/gestures/longpressdraggable/index.dart b/lib/widget/gestures/longpressdraggable/index.dart index eea4d80..1ad6e0e 100644 --- a/lib/widget/gestures/longpressdraggable/index.dart +++ b/lib/widget/gestures/longpressdraggable/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/appbar/index.dart b/lib/widget/navigator/appbar/index.dart index 5fc573c..e4d03f5 100644 --- a/lib/widget/navigator/appbar/index.dart +++ b/lib/widget/navigator/appbar/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/bottomnavigationbar/index.dart b/lib/widget/navigator/bottomnavigationbar/index.dart index d939240..5d08ea3 100644 --- a/lib/widget/navigator/bottomnavigationbar/index.dart +++ b/lib/widget/navigator/bottomnavigationbar/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; import 'demo_with_pageview.dart' as DemoWithPageView; @@ -15,7 +15,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/drawer/index.dart b/lib/widget/navigator/drawer/index.dart index 2581326..ee9f217 100644 --- a/lib/widget/navigator/drawer/index.dart +++ b/lib/widget/navigator/drawer/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/floatingactionbutton/index.dart b/lib/widget/navigator/floatingactionbutton/index.dart index 2812f52..f1f6e7d 100644 --- a/lib/widget/navigator/floatingactionbutton/index.dart +++ b/lib/widget/navigator/floatingactionbutton/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/materialapp/index.dart b/lib/widget/navigator/materialapp/index.dart index 3510d27..6d08875 100644 --- a/lib/widget/navigator/materialapp/index.dart +++ b/lib/widget/navigator/materialapp/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/navigator/index.dart b/lib/widget/navigator/navigator/index.dart index a658146..8c9b6da 100644 --- a/lib/widget/navigator/navigator/index.dart +++ b/lib/widget/navigator/navigator/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/pageview/index.dart b/lib/widget/navigator/pageview/index.dart index 28a357f..bea65a5 100644 --- a/lib/widget/navigator/pageview/index.dart +++ b/lib/widget/navigator/pageview/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/popupmenubutton/index.dart b/lib/widget/navigator/popupmenubutton/index.dart index a4db5a5..1d271a8 100644 --- a/lib/widget/navigator/popupmenubutton/index.dart +++ b/lib/widget/navigator/popupmenubutton/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/scaffold/index.dart b/lib/widget/navigator/scaffold/index.dart index b103bc2..9607612 100644 --- a/lib/widget/navigator/scaffold/index.dart +++ b/lib/widget/navigator/scaffold/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/tabbar/index.dart b/lib/widget/navigator/tabbar/index.dart index 2440c6e..19fb930 100644 --- a/lib/widget/navigator/tabbar/index.dart +++ b/lib/widget/navigator/tabbar/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/tabbarview/index.dart b/lib/widget/navigator/tabbarview/index.dart index 5d680ec..0a7870f 100644 --- a/lib/widget/navigator/tabbarview/index.dart +++ b/lib/widget/navigator/tabbarview/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/navigator/widgetsapp/index.dart b/lib/widget/navigator/widgetsapp/index.dart index 2d8994a..94c49a0 100644 --- a/lib/widget/navigator/widgetsapp/index.dart +++ b/lib/widget/navigator/widgetsapp/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/align/index.dart b/lib/widget/regular/align/index.dart index 49bb139..e2e3ada 100644 --- a/lib/widget/regular/align/index.dart +++ b/lib/widget/regular/align/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/aspectratio/index.dart b/lib/widget/regular/aspectratio/index.dart index 82dee17..871b123 100644 --- a/lib/widget/regular/aspectratio/index.dart +++ b/lib/widget/regular/aspectratio/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/center/index.dart b/lib/widget/regular/center/index.dart index 169e581..ec3cebd 100644 --- a/lib/widget/regular/center/index.dart +++ b/lib/widget/regular/center/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/column/index.dart b/lib/widget/regular/column/index.dart index 37252df..c2abab7 100644 --- a/lib/widget/regular/column/index.dart +++ b/lib/widget/regular/column/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; import 'demo_expanded.dart' as DemoExpanded; @@ -15,7 +15,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/constrainedbox/index.dart b/lib/widget/regular/constrainedbox/index.dart index a299358..81afbc4 100644 --- a/lib/widget/regular/constrainedbox/index.dart +++ b/lib/widget/regular/constrainedbox/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; import 'demo_expand.dart' as DemoExpand; @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/container/index.dart b/lib/widget/regular/container/index.dart index 29b8144..645f282 100644 --- a/lib/widget/regular/container/index.dart +++ b/lib/widget/regular/container/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; @@ -15,7 +15,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/fittedbox/index.dart b/lib/widget/regular/fittedbox/index.dart index 7d7679d..a96192c 100644 --- a/lib/widget/regular/fittedbox/index.dart +++ b/lib/widget/regular/fittedbox/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/flow/index.dart b/lib/widget/regular/flow/index.dart index 31d6a37..b514358 100644 --- a/lib/widget/regular/flow/index.dart +++ b/lib/widget/regular/flow/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/layoutbuilder/index.dart b/lib/widget/regular/layoutbuilder/index.dart index 5e68566..00c3cf2 100644 --- a/lib/widget/regular/layoutbuilder/index.dart +++ b/lib/widget/regular/layoutbuilder/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/listbody/index.dart b/lib/widget/regular/listbody/index.dart index d23736d..3ffda1c 100644 --- a/lib/widget/regular/listbody/index.dart +++ b/lib/widget/regular/listbody/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/listview/index.dart b/lib/widget/regular/listview/index.dart index 2b3b38f..a0c98b9 100644 --- a/lib/widget/regular/listview/index.dart +++ b/lib/widget/regular/listview/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; import 'demo_builder.dart' as DemoBuilder; import 'demo_separated.dart' as DemoSeparated; @@ -16,7 +16,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/padding/index.dart b/lib/widget/regular/padding/index.dart index 7270004..d5e4824 100644 --- a/lib/widget/regular/padding/index.dart +++ b/lib/widget/regular/padding/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/row/index.dart b/lib/widget/regular/row/index.dart index bfc97b0..413f4f6 100644 --- a/lib/widget/regular/row/index.dart +++ b/lib/widget/regular/row/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; import 'demo_expanded.dart' as DemoExpanded; @@ -16,7 +16,7 @@ class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/stack/index.dart b/lib/widget/regular/stack/index.dart index 4962068..b2e0632 100644 --- a/lib/widget/regular/stack/index.dart +++ b/lib/widget/regular/stack/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/table/index.dart b/lib/widget/regular/table/index.dart index d6f4768..79afd07 100644 --- a/lib/widget/regular/table/index.dart +++ b/lib/widget/regular/table/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/regular/wrap/index.dart b/lib/widget/regular/wrap/index.dart index c117e8a..e8b2a7f 100644 --- a/lib/widget/regular/wrap/index.dart +++ b/lib/widget/regular/wrap/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -13,7 +13,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/scrollview/customscrollview/index.dart b/lib/widget/scrollview/customscrollview/index.dart index d8768c7..1298839 100644 --- a/lib/widget/scrollview/customscrollview/index.dart +++ b/lib/widget/scrollview/customscrollview/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo_custom_scrollview.dart' as DemoCustomScrollview; import 'demo_custom_scrollview_pinned.dart' as DemoCustomScrollviewPinned; import 'demo_custom_scrollview_floating.dart' as DemoCustomScrollviewFloating; @@ -17,7 +17,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/scrollview/gridview/index.dart b/lib/widget/scrollview/gridview/index.dart index bb5c505..b4a3647 100644 --- a/lib/widget/scrollview/gridview/index.dart +++ b/lib/widget/scrollview/gridview/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo_count.dart' as DemoCount; import 'demo_extent.dart' as DemoExtent; import 'demo_custom.dart' as DemoCustom; @@ -17,7 +17,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/scrollview/listview/index.dart b/lib/widget/scrollview/listview/index.dart index 6ecbd22..8f0ad07 100644 --- a/lib/widget/scrollview/listview/index.dart +++ b/lib/widget/scrollview/listview/index.dart @@ -4,7 +4,7 @@ * Email: 1476589247@qq.com */ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -20,7 +20,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/scrollview/nestedscrollview/index.dart b/lib/widget/scrollview/nestedscrollview/index.dart index 2a135eb..c512c2e 100644 --- a/lib/widget/scrollview/nestedscrollview/index.dart +++ b/lib/widget/scrollview/nestedscrollview/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/scrollview/scrollable/index.dart b/lib/widget/scrollview/scrollable/index.dart index cbd81b8..fec8be9 100644 --- a/lib/widget/scrollview/scrollable/index.dart +++ b/lib/widget/scrollview/scrollable/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/scrollview/scrollbar/index.dart b/lib/widget/scrollview/scrollbar/index.dart index 952d397..cbc9ce7 100644 --- a/lib/widget/scrollview/scrollbar/index.dart +++ b/lib/widget/scrollview/scrollbar/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/scrollview/scrollcontroller/index.dart b/lib/widget/scrollview/scrollcontroller/index.dart index 96bcbe0..3e2f788 100644 --- a/lib/widget/scrollview/scrollcontroller/index.dart +++ b/lib/widget/scrollview/scrollcontroller/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/scrollview/singlechildscrollview/index.dart b/lib/widget/scrollview/singlechildscrollview/index.dart index 89b5d3d..a7c838b 100644 --- a/lib/widget/scrollview/singlechildscrollview/index.dart +++ b/lib/widget/scrollview/singlechildscrollview/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo_single_child_scrollview.dart' as DemoSingleChildScrollview; import 'demo_single_child_scrollview_overflow.dart' as DemoSingleChildScrollviewOverflow; @@ -15,7 +15,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/backdropfilter/index.dart b/lib/widget/vision/backdropfilter/index.dart index b7df93b..aea31ee 100644 --- a/lib/widget/vision/backdropfilter/index.dart +++ b/lib/widget/vision/backdropfilter/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/clipoval/index.dart b/lib/widget/vision/clipoval/index.dart index 4366620..5bce5f1 100644 --- a/lib/widget/vision/clipoval/index.dart +++ b/lib/widget/vision/clipoval/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/clippath/index.dart b/lib/widget/vision/clippath/index.dart index 0841ea1..2ce237c 100644 --- a/lib/widget/vision/clippath/index.dart +++ b/lib/widget/vision/clippath/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/cliprect/index.dart b/lib/widget/vision/cliprect/index.dart index a48fcf3..a79f3fc 100644 --- a/lib/widget/vision/cliprect/index.dart +++ b/lib/widget/vision/cliprect/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/custompaint/index.dart b/lib/widget/vision/custompaint/index.dart index fbc29a2..0b1d06f 100644 --- a/lib/widget/vision/custompaint/index.dart +++ b/lib/widget/vision/custompaint/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/decoratedbox/index.dart b/lib/widget/vision/decoratedbox/index.dart index d82d54c..8acae39 100644 --- a/lib/widget/vision/decoratedbox/index.dart +++ b/lib/widget/vision/decoratedbox/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/fractionaltranslation/index.dart b/lib/widget/vision/fractionaltranslation/index.dart index f3243a9..66e810d 100644 --- a/lib/widget/vision/fractionaltranslation/index.dart +++ b/lib/widget/vision/fractionaltranslation/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/mediaquery/index.dart b/lib/widget/vision/mediaquery/index.dart index 3d4ba89..4b4260e 100644 --- a/lib/widget/vision/mediaquery/index.dart +++ b/lib/widget/vision/mediaquery/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/opacity/index.dart b/lib/widget/vision/opacity/index.dart index 701c7a3..dfa1cb1 100644 --- a/lib/widget/vision/opacity/index.dart +++ b/lib/widget/vision/opacity/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/rotatedbox/index.dart b/lib/widget/vision/rotatedbox/index.dart index 9a7f83c..2bc5417 100644 --- a/lib/widget/vision/rotatedbox/index.dart +++ b/lib/widget/vision/rotatedbox/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/theme/index.dart b/lib/widget/vision/theme/index.dart index 8b6c1e7..5537e4c 100644 --- a/lib/widget/vision/theme/index.dart +++ b/lib/widget/vision/theme/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/lib/widget/vision/transform/index.dart b/lib/widget/vision/transform/index.dart index 11caef6..6109be9 100644 --- a/lib/widget/vision/transform/index.dart +++ b/lib/widget/vision/transform/index.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:efox_flutter/components/widgetComp.dart' as WidgetComp; +import 'package:efox_flutter/components/widget_comp.dart' as widget_comp; import 'demo.dart' as Demo; class Index extends StatefulWidget { @@ -14,7 +14,7 @@ class Index extends StatefulWidget { class _IndexState extends State { @override Widget build(BuildContext context) { - return WidgetComp.Index( + return widget_comp.Index( title: Index.title, originCodeUrl: Index.originCodeUrl, mdUrl: Index.mdUrl, diff --git a/readme/develop.md b/readme/develop.md new file mode 100644 index 0000000..93936af --- /dev/null +++ b/readme/develop.md @@ -0,0 +1,200 @@ +# Flutter UI V1.0.0 代码规范 + +### 文件命名规范 +##### 文件夹小写“单”词,复合词建议用中划线隔开 +``` +lib + |--components + |--page + |--app-login +``` + +*不建议以下写法 * +``` +lib + |--common_component 【X】 + |--commonComponent 【X】 + |--homepage 【X】 +``` +##### 文件名小写,多词建议用下划线 +``` +lib + |--components + |--webview_component.dart + |--page +``` + +*不建议以下写法 * +``` +lib + |--commonComponent.dart 【X】 + |--home-page.dart 【X】 +``` + +----------------------- + +### 引入库、类规范 + +##### 优先引入dart库 > 第三方工具包 > 本地工具包 > 其它包或类文件 +``` +/// 默认库 +import 'dart:async'; +import 'dart:material'; +/// 第三方工具包 +import 'package:third-packages.dart'; +/// 本地工具包 +import 'package:page/utils.dart'; +/// 其它类文件 +import 'package:page/app-login.dart'; +``` + +##### 优先使用相对路径,引用外层目录时,建议使用绝对路径 +``` +/// 相对路径 +import 'utils.dart'; +/// 绝对文件 +import 'package:efox_flutter/utils/local_storage.dart'; +``` + +##### 使用库时,建议增加show、as方式标明引用对象,同时命名使用大驼峰 +``` +import 'utils.dart' as Utils; +/// 绝对文件 +import 'package:efox_flutter/utils/local_storage.dart' show LocalStorage; +``` + +----------------------- + +### 类命名规范 + +##### 类名建议使用大驼峰命名 +``` +class DemoClass { + const DemoClass([arg]); +} + +@DemoClass(anArg) +class A { ... } +``` + +----------------------- + +### 变量命名规范 +##### 常量建议使用小驼峰进行命名 +``` +const pi = 3.14; +final numberReg = RegExp('\d+'); +var name; +HttpRequest httpRequest; +var getName (int id) { + // ... +} +``` + +*不推荐如下写法 * + +``` +constPI = 3.14; +const NumberReg = RegExp('\d+'); + +class Animal { + static final TYPE = 'Animal'; +} +``` +##### 变量默认值为null时,不需要赋值 +``` +var name; +``` +*不建议写法 * +``` +var name = null; +``` + +----------------------- + +### 流程控制 +##### 减少else使用 +``` +if (name == null) return +print('user name is $name') +``` + +##### 减少拼接符操作,建议使用“\$"引入,类引用时通过“\${}”方式引用 +``` +var name = 'tiger' +User user = new User(name: 'kings', age: 12) +print('test name is $name') +print('user name is ${user.name}') +print('their are $name and ${user.name}) +``` + +----------------------- + +### 注释 +##### 代码行内注释建议使用// +``` +String getName (User user) { + // return user's name + return user.name; +} +``` +##### 变量注释建议使用/// +``` +/// 定义常量值 +const pi = 3.14; +``` + +----------------------- + +### 类规范 + +##### 构造函数建议使用this直接引用变量 + +``` +class Point { + num x, y; + Point(this.x, this.y); +} +``` + +##### 减少非必要this使用 +``` +class Person { + var age; + + void setPerson (User user) { + setAge(user.age); + } + + void update(age) { + this.age= age; + } +} +``` + +##### 建议减少new写法 +``` +Widget build(BuildContext context) { + return Center( + child: Text('content'), + ); +} +``` + +----------------------- + +### Future使用 + +##### 建议使用async/await + +``` +Future request(String path) async { + var response = await dio.get(path); + return response; +} +``` + +----------------------- + + + From 9859cc462c052517bc30812fd05645b5a0d5db37 Mon Sep 17 00:00:00 2001 From: wanwu <609780590@qq.com> Date: Wed, 3 Apr 2019 17:05:15 +0800 Subject: [PATCH 059/100] =?UTF-8?q?modify=EF=BC=9A=E4=BF=AE=E6=94=B9develo?= =?UTF-8?q?p.md=E6=96=87=E4=BB=B6=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme/develop.md | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/readme/develop.md b/readme/develop.md index 93936af..289dca9 100644 --- a/readme/develop.md +++ b/readme/develop.md @@ -4,34 +4,33 @@ ##### 文件夹小写“单”词,复合词建议用中划线隔开 ``` lib - |--components - |--page - |--app-login + |--components + |--page + |--app-login ``` *不建议以下写法 * ``` lib - |--common_component 【X】 - |--commonComponent 【X】 - |--homepage 【X】 + |--common_component 【X】 + |--commonComponent 【X】 + |--homepage 【X】 ``` ##### 文件名小写,多词建议用下划线 ``` lib - |--components - |--webview_component.dart - |--page + |--components + |--webview_component.dart + |--page ``` *不建议以下写法 * ``` lib - |--commonComponent.dart 【X】 - |--home-page.dart 【X】 + |--commonComponent.dart 【X】 + |--home-page.dart 【X】 ``` ------------------------ ### 引入库、类规范 @@ -63,7 +62,6 @@ import 'utils.dart' as Utils; import 'package:efox_flutter/utils/local_storage.dart' show LocalStorage; ``` ------------------------ ### 类命名规范 @@ -77,7 +75,6 @@ class DemoClass { class A { ... } ``` ------------------------ ### 变量命名规范 ##### 常量建议使用小驼峰进行命名 @@ -110,7 +107,6 @@ var name; var name = null; ``` ------------------------ ### 流程控制 ##### 减少else使用 @@ -128,7 +124,6 @@ print('user name is ${user.name}') print('their are $name and ${user.name}) ``` ------------------------ ### 注释 ##### 代码行内注释建议使用// @@ -144,7 +139,6 @@ String getName (User user) { const pi = 3.14; ``` ------------------------ ### 类规范 @@ -181,7 +175,6 @@ Widget build(BuildContext context) { } ``` ------------------------ ### Future使用 @@ -194,7 +187,6 @@ Future request(String path) async { } ``` ------------------------ From a700241f213e3603532be7d382737134795a5549 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Wed, 3 Apr 2019 17:14:00 +0800 Subject: [PATCH 060/100] =?UTF-8?q?modify:=E4=BF=AE=E6=94=B9=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/{app-login => app_login}/index.dart | 0 lib/page/home.dart | 2 +- readme/develop.md | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) rename lib/page/{app-login => app_login}/index.dart (100%) diff --git a/lib/page/app-login/index.dart b/lib/page/app_login/index.dart similarity index 100% rename from lib/page/app-login/index.dart rename to lib/page/app_login/index.dart diff --git a/lib/page/home.dart b/lib/page/home.dart index f807b03..34f7387 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -4,7 +4,7 @@ import 'package:efox_flutter/controller/index.dart' as Controller; import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'component/tabs.dart' as TabIndex; import 'mine/index.dart' as MyIndex; -import 'app-login/index.dart' as LoginIndex; +import 'app_login/index.dart' as LoginIndex; import 'package:efox_flutter/store/index.dart' show Store, UserModel; diff --git a/readme/develop.md b/readme/develop.md index 289dca9..d2b8df6 100644 --- a/readme/develop.md +++ b/readme/develop.md @@ -1,18 +1,18 @@ # Flutter UI V1.0.0 代码规范 ### 文件命名规范 -##### 文件夹小写“单”词,复合词建议用中划线隔开 +##### 文件夹小写“单”词,复合词建议用下划线隔开 ``` lib |--components |--page - |--app-login + |--app_login ``` *不建议以下写法 * ``` lib - |--common_component 【X】 + |--common-component 【X】 |--commonComponent 【X】 |--homepage 【X】 ``` From 03fa994778fb22614a68b61d4a0a76e3152997e3 Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 3 Apr 2019 18:01:09 +0800 Subject: [PATCH 061/100] feat: IconButton --- docs/widget/common/iconbutton/index.md | 33 +++++++++++++++++- lib/widget/common/iconbutton/demo.dart | 48 ++++++++++++++++++++++++-- readme/widget_progress.md | 2 +- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/docs/widget/common/iconbutton/index.md b/docs/widget/common/iconbutton/index.md index 83532f6..3b6be3d 100644 --- a/docs/widget/common/iconbutton/index.md +++ b/docs/widget/common/iconbutton/index.md @@ -1 +1,32 @@ -## **文档完善中** \ No newline at end of file +## **IconButton** +> +图标按钮,通过填充颜色(墨水)对触摸做出反应 + +### 构造方法 +``` dart +IconButton({ + Key key, + this.iconSize = 24.0, + this.padding = const EdgeInsets.all(8.0), + this.alignment = Alignment.center, + @required this.icon, + this.color, + this.highlightColor, + this.splashColor, + this.disabledColor, + @required this.onPressed, + this.tooltip + }) +``` + +### 属性介绍 +* iconSize = 24.0:图标大小 +* padding = const EdgeInsets.all(8.0):按钮图标周围的填充 +* alignment = Alignment.center:图标对齐方式 +* icon:图标 +* color:图标颜色 +* highlightColor:按钮处于按下时按钮的辅助颜色 +* splashColor:按钮处于按下状态时按钮颜色 +* disabledColor:图标被禁用时按钮内图标的颜色 +* onPressed:点击回调 +* tooltip:长按文本提示 \ No newline at end of file diff --git a/lib/widget/common/iconbutton/demo.dart b/lib/widget/common/iconbutton/demo.dart index e359d57..477a135 100644 --- a/lib/widget/common/iconbutton/demo.dart +++ b/lib/widget/common/iconbutton/demo.dart @@ -6,6 +6,8 @@ class Index extends StatefulWidget { } class _IndexState extends State { + List data = [58273,58275,59493,57903,58283,58284,57771,58285]; + int index = 0; @override void initState() { super.initState(); @@ -17,9 +19,49 @@ class _IndexState extends State { appBar: AppBar( title: Text('IconButton'), ), - body: Center( - child: Text('更新中'), - ), + body: Container( + padding: EdgeInsets.fromLTRB(20.0, 0, 20.0, 0), + child: ListView( + children: [ + IconButton( + iconSize: 30.0, + padding: EdgeInsets.all(8.0), + alignment: Alignment.center, + icon: Icon(IconData( + data[index], + fontFamily: 'MaterialIcons', + matchTextDirection: true + )), + color: Theme.of(context).primaryColor, + highlightColor: Colors.black12, + splashColor: Theme.of(context).primaryColorLight, + onPressed: (){ + if (index < 7) { + this.setState(() { + index = index + 1; + }); + } else { + this.setState(() { + index = 0; + }); + } + }, + tooltip: '长按文本提示', + ), + IconButton( + iconSize: 30.0, + padding: EdgeInsets.all(8.0), + alignment: Alignment.center, + icon: Icon(Icons.sync_disabled), + color: Theme.of(context).primaryColor, + highlightColor: Colors.black12, + splashColor: Theme.of(context).primaryColorLight, + tooltip: '长按文本提示', + disabledColor: Colors.grey + ) + ], + ), + ) ); } } diff --git a/readme/widget_progress.md b/readme/widget_progress.md index 9672697..e37b610 100644 --- a/readme/widget_progress.md +++ b/readme/widget_progress.md @@ -54,7 +54,7 @@ Flutter UI │ ├─divider 【✔️ v1.0】 │ ├─flatbutton 【✔️ v1.0】 │ ├─icon 【✔️ v1.0】 - │ ├─iconbutton + │ ├─iconbutton 【✔️ v1.0】 │ ├─image 【✔️ v1.0】 │ ├─listtile 【✔️ v1.0】 │ ├─placeholder From 61e2463b5221b3afa09ad75d799d04969858daa3 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Wed, 3 Apr 2019 18:21:40 +0800 Subject: [PATCH 062/100] =?UTF-8?q?feat=EF=BC=9A=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/app_login/index.dart | 12 ++--- lib/page/home.dart | 91 +++++++++++++++++++---------------- locale/en.json | 12 ++++- locale/zh.json | 8 +++ readme/develop.md | 8 +-- 5 files changed, 78 insertions(+), 53 deletions(-) diff --git a/lib/page/app_login/index.dart b/lib/page/app_login/index.dart index 9c261da..47d1d6b 100644 --- a/lib/page/app_login/index.dart +++ b/lib/page/app_login/index.dart @@ -39,24 +39,24 @@ class _IndexState extends State { controller: nameCtl, autofocus: true, decoration: InputDecoration( - labelText: '账户名', - hintText: '请输入Github账户名', + labelText: AppLocalizations.$t('login.account'), + hintText: AppLocalizations.$t('login.account_tips'), icon: Icon(Icons.person), ), validator: (v) { - return v.trim().length > 0 ? null : '用户名不能为空'; + return v.trim().length > 0 ? null : AppLocalizations.$t('login.account_error_tips'); }, ), TextFormField( controller: pwdCtl, decoration: InputDecoration( - labelText: '密码', - hintText: '请输入登录密码', + labelText: AppLocalizations.$t('login.password'), + hintText: AppLocalizations.$t('login.password_tips'), icon: Icon(Icons.lock), ), obscureText: true, validator: (v) { - return v.trim().length > 5 ? null : "密码不能少于6位"; + return v.trim().length > 0 ? null : AppLocalizations.$t('login.password_error_tips'); }, ), Padding( diff --git a/lib/page/home.dart b/lib/page/home.dart index 34f7387..1146d0f 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -47,23 +47,52 @@ class _IndexState extends State { ); } + List renderTiles(id) { + if (id != null) { + return [ + ListTile( + leading: Icon(Icons.exit_to_app), + title: Text(AppLocalizations.$t('common.logout')), + onTap: () { + Store.value(context).$clearUserInfo(); + }, + ) + ]; + } + return [ + ListTile( + leading: Icon(Icons.account_circle), + title: Text(AppLocalizations.$t('common.login')), + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return LoginIndex.Index(); + }, + ), + ); + }, + ) + ]; + } + /** * 抽屉面板 */ renderDrawer() { print('renderDrawer $context'); return Drawer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: 200, - decoration: BoxDecoration( - color: Color(AppTheme.mainColor), - ), - child: Store.connect( - builder: (context, child, model) { - return Row(children: [ + child: Store.connect(builder: (context, child, model) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 200, + decoration: BoxDecoration( + color: Color(AppTheme.mainColor), + ), + child: Row( + children: [ Padding( padding: EdgeInsets.symmetric(horizontal: 20.0), child: ClipOval( @@ -76,40 +105,20 @@ class _IndexState extends State { ), ), Text( - Store.value(context).user.name ?? 'Guest', + model.user.name ?? 'Guest', style: TextStyle(fontWeight: FontWeight.bold), ) - ]); - }, + ], + ), ), - ), - Expanded( - child: ListView( - children: [ - ListTile( - leading: Icon(Icons.account_circle), - title: Text(AppLocalizations.$t('common.login')), - onTap: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (BuildContext context) { - return LoginIndex.Index(); - })); - }, - ), - Divider(), - ListTile( - leading: Icon(Icons.exit_to_app), - title: Text(AppLocalizations.$t('common.logout')), - onTap: () { - Store.value(context).$clearUserInfo(); - // Store.value(context).$getUserInfo(); - }, - ), - ], + Expanded( + child: ListView( + children: renderTiles(model.user.node_id), + ), ), - ), - ], - ), + ], + ); + }), ); } diff --git a/locale/en.json b/locale/en.json index 92e5916..d65bb5e 100644 --- a/locale/en.json +++ b/locale/en.json @@ -2,8 +2,8 @@ "title_component": "Components", "title_my": "My", "common": { - "login": "登录", - "logout": "退出" + "login": "Sign In", + "logout": "Logout" }, "common_mine_1": { "cn": "CN", @@ -19,6 +19,14 @@ "loadNetwork": "Load Network Document Resources", "loadLocal": "Load Local Document Resources" }, + "login": { + "account": "Account", + "account_tips": "Please Input Account", + "account_error_tips": "Account Is Not Empty", + "password": "Password", + "password_tips": "Please Input Password", + "password_error_tips": "Password Is Not Empty" + }, "loading": "Loading", "ScrollComponents": "ScrollComponents", "Common": "Common", diff --git a/locale/zh.json b/locale/zh.json index f2a61b9..6c22b44 100644 --- a/locale/zh.json +++ b/locale/zh.json @@ -19,6 +19,14 @@ "loadNetwork": "网络优良,可选择加载线上文档资源", "loadLocal": "网络较差时,可选择加载本地文档资源" }, + "login": { + "account": "账户名", + "account_tips": "请输入账户名", + "account_error_tips": "用户名不能为空", + "password": "密码", + "password_tips": "请输入登录密码", + "password_error_tips": "密码不能为空" + }, "loading": "加载中", "ScrollComponents": "滚动", "Common": "通用", diff --git a/readme/develop.md b/readme/develop.md index d2b8df6..c511bca 100644 --- a/readme/develop.md +++ b/readme/develop.md @@ -9,7 +9,7 @@ lib |--app_login ``` -*不建议以下写法 * +不建议以下写法 ``` lib |--common-component 【X】 @@ -24,7 +24,7 @@ lib |--page ``` -*不建议以下写法 * +不建议以下写法 ``` lib |--commonComponent.dart 【X】 @@ -88,7 +88,7 @@ var getName (int id) { } ``` -*不推荐如下写法 * +不推荐如下写法 ``` constPI = 3.14; @@ -102,7 +102,7 @@ class Animal { ``` var name; ``` -*不建议写法 * +不建议写法 ``` var name = null; ``` From 6f86796ad9eb8f918517a5995d76f8e84253ca7b Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Thu, 4 Apr 2019 18:53:04 +0800 Subject: [PATCH 063/100] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0assetbundle?= =?UTF-8?q?=E3=80=81=E4=BC=98=E5=8C=96=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/widget/common/assetbundle/index.md | 14 +- lib/controller/index.dart | 1 - lib/http/index.dart | 2 +- lib/main.dart | 3 +- lib/mock/index.dart | 5 + lib/mock/login.dart | 13 ++ lib/page/app_login/index.dart | 269 +++++++++++++++--------- lib/page/home.dart | 4 +- lib/store/models/user_model.dart | 27 ++- lib/store/objects/user_info.dart | 182 ++++++++-------- lib/widget/common/assetbundle/demo.dart | 19 +- 11 files changed, 340 insertions(+), 199 deletions(-) create mode 100644 lib/mock/index.dart create mode 100644 lib/mock/login.dart diff --git a/docs/widget/common/assetbundle/index.md b/docs/widget/common/assetbundle/index.md index 83532f6..eab9b0d 100644 --- a/docs/widget/common/assetbundle/index.md +++ b/docs/widget/common/assetbundle/index.md @@ -1 +1,13 @@ -## **文档完善中** \ No newline at end of file +## **AssetBundle** +> 抽象类,资源读取 + +#### 使用 +``` +import 'package:flutter/services.dart' show rootBundle; + +getText() async { + String text = await rootBundle.loadString('locale/zh.json'); + print('text $text'); +} + +``` \ No newline at end of file diff --git a/lib/controller/index.dart b/lib/controller/index.dart index be71ebb..2e839a1 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -6,7 +6,6 @@ import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; void initState() { // 获取版本号 Store.valueNotCtx().$getAppVersion(); - // Store.valueNotCtx().$getUserInfo(); // 登录 Store.valueNotCtx().$getLocalUserInfo(); Future.delayed(Duration(seconds: 3), () { diff --git a/lib/http/index.dart b/lib/http/index.dart index cb27f28..6e0dbba 100644 --- a/lib/http/index.dart +++ b/lib/http/index.dart @@ -44,7 +44,7 @@ Dio getDio({options, loading}) { print('=========【请求成功】Start============'); print("请求地址 ${response.request.uri}"); print("请求头 ${response.statusCode}"); - print("请求参数 ${response.data}"); + print("返回值 ${response.data}"); print('=========【请求成功】End============'); await AppLoading.afterResponse(response.request.uri, loading); return response; diff --git a/lib/main.dart b/lib/main.dart index 796e775..3e18e27 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,10 +8,11 @@ import 'package:efox_flutter/store/index.dart' import 'package:efox_flutter/router/index.dart' show FluroRouter; //路由 import 'package:efox_flutter/config/theme.dart' show AppTheme; //主题 import 'package:efox_flutter/utils/analytics.dart' as Analytics; //统计 - +// import './mock/index.dart' as TestCase; class MainApp extends StatefulWidget { MainApp() { FluroRouter.initRouter(); + // TestCase.runTestCase(); } @override diff --git a/lib/mock/index.dart b/lib/mock/index.dart new file mode 100644 index 0000000..85054bb --- /dev/null +++ b/lib/mock/index.dart @@ -0,0 +1,5 @@ +import 'login.dart' as LoginTest; + +runTestCase () async { + await LoginTest.testLogin(); +} \ No newline at end of file diff --git a/lib/mock/login.dart b/lib/mock/login.dart new file mode 100644 index 0000000..9a398d3 --- /dev/null +++ b/lib/mock/login.dart @@ -0,0 +1,13 @@ +import 'package:efox_flutter/store/objects/user_info.dart' show UserInfo; +const user = { + "id": 123, + "plan": { + 'name': '123' + } +}; + +testLogin () { + UserInfo _user = UserInfo.fromJson(user); + print('_user $_user'); + print('_user ${_user.plan.name}'); +} \ No newline at end of file diff --git a/lib/page/app_login/index.dart b/lib/page/app_login/index.dart index 47d1d6b..9a8c516 100644 --- a/lib/page/app_login/index.dart +++ b/lib/page/app_login/index.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'dart:math' as math; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/store/index.dart' show Store, UserModel; @@ -14,116 +15,190 @@ class _IndexState extends State { TextEditingController pwdCtl = TextEditingController(text: ''); GlobalKey _formKey = GlobalKey(); + OverlayState _overlayState; + OverlayEntry _overlayEntry; + + /** + * 弹窗内容 + */ + renderOverlay(String text) { + _overlayEntry?.remove(); + _overlayState = Overlay.of(context); + _overlayEntry = OverlayEntry(builder: (context) { + return Center(child: Text(text)); + }); + _overlayState.insert(_overlayEntry); + } + + /** + * 顶部图标 + */ + renderGithubImage() { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + 'assets/imgs/github_2.png', + width: 50, + height: 50, + ), + SizedBox( + width: 10, + ), + Transform.rotate( + child: Icon( + Icons.import_export, + color: Colors.black, + ), + angle: math.pi / 2, + ), + SizedBox( + width: 10, + ), + Image.asset( + 'assets/imgs/github_1.png', + width: 50, + height: 50, + ), + ], + ); + } + @override Widget build(BuildContext ctx) { - return Scaffold( - appBar: AppBar( - centerTitle: true, - title: Text( - AppLocalizations.$t('common.login'), - textAlign: TextAlign.center, + return WillPopScope( + child: Scaffold( + appBar: AppBar( + centerTitle: true, + title: Text( + AppLocalizations.$t('common.login'), + textAlign: TextAlign.center, + ), + automaticallyImplyLeading: false, ), - automaticallyImplyLeading: false, - ), - body: Builder(builder: (BuildContext context) { - // Store.setWidgetCtx(context); - return SingleChildScrollView( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 50, horizontal: 24), - child: Form( - key: _formKey, - autovalidate: true, - child: Column( - children: [ - TextFormField( - controller: nameCtl, - autofocus: true, - decoration: InputDecoration( - labelText: AppLocalizations.$t('login.account'), - hintText: AppLocalizations.$t('login.account_tips'), - icon: Icon(Icons.person), + body: Builder(builder: (BuildContext context) { + return SingleChildScrollView( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 50, horizontal: 24), + child: Form( + key: _formKey, + autovalidate: true, + child: Column( + children: [ + renderGithubImage(), + TextFormField( + controller: nameCtl, + autofocus: true, + decoration: InputDecoration( + labelText: AppLocalizations.$t('login.account'), + hintText: AppLocalizations.$t('login.account_tips'), + icon: Icon(Icons.person), + ), + validator: (v) { + return v.trim().length > 0 + ? null + : AppLocalizations.$t('login.account_error_tips'); + }, + ), + TextFormField( + controller: pwdCtl, + decoration: InputDecoration( + labelText: AppLocalizations.$t('login.password'), + hintText: AppLocalizations.$t('login.password_tips'), + icon: Icon(Icons.lock), + ), + obscureText: true, + validator: (v) { + return v.trim().length > 0 + ? null + : AppLocalizations.$t('login.password_error_tips'); + }, + ), + Padding( + padding: EdgeInsets.only(top: 50), + child: Row( + children: [ + Expanded( + child: RaisedButton( + padding: EdgeInsets.all(15), + color: Theme.of(context).primaryColor, + textColor: Theme.of(context) + .primaryTextTheme + .title + .color, + child: Text( + AppLocalizations.$t('common.login'), + ), + onPressed: () async { + if ((_formKey.currentState as FormState) + .validate()) { + await Store.value(context) + .$loginController(context, { + 'name': nameCtl.text, + 'pwd': pwdCtl.text + }); + } + }, + ), + ) + ], + ), ), - validator: (v) { - return v.trim().length > 0 ? null : AppLocalizations.$t('login.account_error_tips'); - }, - ), - TextFormField( - controller: pwdCtl, - decoration: InputDecoration( - labelText: AppLocalizations.$t('login.password'), - hintText: AppLocalizations.$t('login.password_tips'), - icon: Icon(Icons.lock), + SizedBox( + height: 10, ), - obscureText: true, - validator: (v) { - return v.trim().length > 0 ? null : AppLocalizations.$t('login.password_error_tips'); - }, - ), - Padding( - padding: EdgeInsets.only(top: 50), - child: Row( + Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Expanded( - child: RaisedButton( - padding: EdgeInsets.all(15), - color: Theme.of(context).primaryColor, - textColor: - Theme.of(context).primaryTextTheme.title.color, - child: Text( - AppLocalizations.$t('common.login'), + GestureDetector( + child: Text( + 'Github账户登录说明', + style: TextStyle( + decoration: TextDecoration.underline, + textBaseline: TextBaseline.ideographic, + decorationColor: Color(0xff000000), ), - onPressed: () async { - if ((_formKey.currentState as FormState) - .validate()) { - Store.value(context) - .$loginController(context, { - 'name': nameCtl.text, - 'pwd': pwdCtl.text - }); - } - }, - ), - ) - ], - ), - ), - SizedBox( - height: 10, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - GestureDetector( - child: Text( - 'Github账户登录说明', - style: TextStyle( - decoration: TextDecoration.underline, - textBaseline: TextBaseline.ideographic, - decorationColor: Color(0xff000000), ), + onTap: () { + renderOverlay(" Text 1"); + }, ), - ), - SizedBox( - width: 10, - ), - GestureDetector( - child: Text( - '软件许可及服务协议', - style: TextStyle( - decoration: TextDecoration.underline, - textBaseline: TextBaseline.ideographic, - decorationColor: const Color(0xff000000), + SizedBox( + width: 10, + ), + GestureDetector( + child: Text( + '软件许可及服务协议', + style: TextStyle( + decoration: TextDecoration.underline, + textBaseline: TextBaseline.ideographic, + decorationColor: const Color(0xff000000), + ), ), + onTap: () { + renderOverlay(" Text 2"); + }, ), - ), - ], - ) - ], + ], + ) + ], + ), ), ), - ), - ); - }), + ); + }), + ), + onWillPop: () { + beforeDispose(); + }, ); } + beforeDispose() { + if (_overlayEntry != null) { + _overlayEntry.remove(); + _overlayEntry = null; + } else { + Navigator.of(context).pop(); + } + } } diff --git a/lib/page/home.dart b/lib/page/home.dart index 1146d0f..d323ae6 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -56,7 +56,7 @@ class _IndexState extends State { onTap: () { Store.value(context).$clearUserInfo(); }, - ) + ), ]; } return [ @@ -113,7 +113,7 @@ class _IndexState extends State { ), Expanded( child: ListView( - children: renderTiles(model.user.node_id), + children: renderTiles(model.user.id), ), ), ], diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index 6d78224..e017b36 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -1,22 +1,35 @@ +import 'package:flutter/material.dart'; +import 'dart:convert'; +import 'package:flutter/foundation.dart' show ChangeNotifier; import 'package:dio/dio.dart' show Options; import '../objects/user_info.dart' show UserInfo; import '../objects/github_resp_info.dart' show GitHubRespInfo; import 'package:efox_flutter/http/index.dart' as Http; -import 'dart:convert'; -import 'package:flutter/foundation.dart' show ChangeNotifier; import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; -import 'package:flutter/material.dart'; +import 'package:efox_flutter/http/loading.dart' as Loading; class UserModel with ChangeNotifier { UserInfo user = UserInfo(); + Future testLogin() async { + await Loading.beforeRequest('aaa', {}); + return Future.delayed(Duration(seconds: 3), () async { + await Loading.afterResponse('aaa', {}); + print("返回中"); + return true; + }); + } + /** * 登录控制 */ Future $loginController(context, payload) async { dynamic result = await $login(payload); + print('返回result $result'); if (result == true) { + print('登录成功后退'); Navigator.of(context).pop(); } else { + print('登录失败'); Scaffold.of(context).showSnackBar(new SnackBar( content: new Text('登录失败'), )); @@ -78,11 +91,12 @@ class UserModel with ChangeNotifier { */ $getLocalUserInfo() async { String data = await LocalStorage.get('githubUserInfo'); + print("本地数据 $data"); if (data == null) { $getUserInfo(); return; } - UserInfo user = UserInfo.fromJson(data); + UserInfo user = UserInfo.fromJson(json.decode(data)); $setUserInfo(user); } @@ -91,7 +105,9 @@ class UserModel with ChangeNotifier { */ $setUserInfo(payload) { user = payload; - LocalStorage.set('githubUserInfo', json.encode(payload)); + if (user != null && user.id != null) { + LocalStorage.set('githubUserInfo', json.encode(user)); + } notifyListeners(); } @@ -100,6 +116,7 @@ class UserModel with ChangeNotifier { */ $clearUserInfo() { user = UserInfo(); + LocalStorage.remove('githubUserInfo'); LocalStorage.remove('githubRespInfo'); LocalStorage.remove('githubRespLoginToken'); notifyListeners(); diff --git a/lib/store/objects/user_info.dart b/lib/store/objects/user_info.dart index 07dd61a..6382da0 100644 --- a/lib/store/objects/user_info.dart +++ b/lib/store/objects/user_info.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + class UserInfo extends Object { String login; num id; @@ -36,7 +38,7 @@ class UserInfo extends Object { num disk_usage; num collaborators; bool two_factor_authentication; - dynamic plan; + Plan plan; UserInfo({ this.login, @@ -79,86 +81,86 @@ class UserInfo extends Object { this.plan, }); - UserInfo.fromJson(json) { - login = json['login']; - id = json['id']; - node_id = json['node_id']; - avatar_url = json['avatar_url']; - gravatar_id = json['gravatar_id']; - url = json['url']; - html_url = json['html_url']; - followers_url = json['followers_url']; - following_url = json['following_url']; - gists_url = json['gists_url']; - starred_url = json['starred_url']; - subscriptions_url = json['subscriptions_url']; - organizations_url = json['organizations_url']; - repos_url = json['repos_url']; - events_url = json['events_url']; - received_events_url = json['received_events_url']; - type = json['type']; - site_admin = json['site_admin']; - name = json['name']; - company = json['company']; - blog = json['blog']; - location = json['location']; - email = json['email']; - hireable = json['hireable']; - bio = json['bio']; - public_repos = json['public_repos']; - public_gists = json['public_gists']; - followers = json['followers']; - following = json['following']; - created_at = json['created_at']; - updated_at = json['updated_at']; - private_gists = json['private_gists']; - total_private_repos = json['total_private_repos']; - owned_private_repos = json['owned_private_repos']; - disk_usage = json['disk_usage']; - collaborators = json['collaborators']; - two_factor_authentication = json['two_factor_authentication']; - plan = Plan.fromJson(json['plan']); + UserInfo.fromJson(instance) { + login = instance['login']; + id = instance['id']; + node_id = instance['node_id']; + avatar_url = instance['avatar_url']; + gravatar_id = instance['gravatar_id']; + url = instance['url']; + html_url = instance['html_url']; + followers_url = instance['followers_url']; + following_url = instance['following_url']; + gists_url = instance['gists_url']; + starred_url = instance['starred_url']; + subscriptions_url = instance['subscriptions_url']; + organizations_url = instance['organizations_url']; + repos_url = instance['repos_url']; + events_url = instance['events_url']; + received_events_url = instance['received_events_url']; + type = instance['type']; + site_admin = instance['site_admin']; + name = instance['name']; + company = instance['company']; + blog = instance['blog']; + location = instance['location']; + email = instance['email']; + hireable = instance['hireable']; + bio = instance['bio']; + public_repos = instance['public_repos']; + public_gists = instance['public_gists']; + followers = instance['followers']; + following = instance['following']; + created_at = instance['created_at']; + updated_at = instance['updated_at']; + private_gists = instance['private_gists']; + total_private_repos = instance['total_private_repos']; + owned_private_repos = instance['owned_private_repos']; + disk_usage = instance['disk_usage']; + collaborators = instance['collaborators']; + two_factor_authentication = instance['two_factor_authentication']; + plan = Plan.fromJson(json.decode(instance['plan'])); } - Map toJson(instance) => { - 'login': instance.login, - 'id': instance.id, - 'node_id': instance.node_id, - 'avatar_url': instance.avatar_url, - 'gravatar_id': instance.gravatar_id, - 'url': instance.url, - 'html_url': instance.html_url, - 'followers_url': instance.followers_url, - 'following_url': instance.following_url, - 'gists_url': instance.gists_url, - 'starred_url': instance.starred_url, - 'subscriptions_url': instance.subscriptions_url, - 'organizations_url': instance.organizations_url, - 'repos_url': instance.repos_url, - 'events_url': instance.events_url, - 'received_events_url': instance.received_events_url, - 'type': instance.type, - 'site_admin': instance.site_admin, - 'name': instance.name, - 'company': instance.company, - 'blog': instance.blog, - 'location': instance.location, - 'email': instance.email, - 'hireable': instance.hireable, - 'bio': instance.bio, - 'public_repos': instance.public_repos, - 'public_gists': instance.public_gists, - 'followers': instance.followers, - 'following': instance.following, - 'created_at': instance.created_at, - 'updated_at': instance.updated_at, - 'private_gists': instance.private_gists, - 'total_private_repos': instance.total_private_repos, - 'owned_private_repos': instance.owned_private_repos, - 'disk_usage': instance.disk_usage, - 'collaborators': instance.collaborators, - 'two_factor_authentication': instance.two_factor_authentication, - 'plan': instance.plan, + Map toJson() => { + 'login': login, + 'id': id, + 'node_id': node_id, + 'avatar_url': avatar_url, + 'gravatar_id': gravatar_id, + 'url': url, + 'html_url': html_url, + 'followers_url': followers_url, + 'following_url': following_url, + 'gists_url': gists_url, + 'starred_url': starred_url, + 'subscriptions_url': subscriptions_url, + 'organizations_url': organizations_url, + 'repos_url': repos_url, + 'events_url': events_url, + 'received_events_url': received_events_url, + 'type': type, + 'site_admin': site_admin, + 'name': name, + 'company': company, + 'blog': blog, + 'location': location, + 'email': email, + 'hireable': hireable, + 'bio': bio, + 'public_repos': public_repos, + 'public_gists': public_gists, + 'followers': followers, + 'following': following, + 'created_at': created_at, + 'updated_at': updated_at, + 'private_gists': private_gists, + 'total_private_repos': total_private_repos, + 'owned_private_repos': owned_private_repos, + 'disk_usage': disk_usage, + 'collaborators': collaborators, + 'two_factor_authentication': two_factor_authentication, + 'plan': json.encode(plan), }; } @@ -175,17 +177,19 @@ class Plan extends Object { this.private_repos, }); - Plan.fromJson(Map json) { - name = json['name']; - space = json['space']; - collaborators = json['collaborators']; - private_repos = json['private_repos']; + Plan.fromJson(Map instance) { + if (instance != null) { + name = instance['name']; + space = instance['space']; + collaborators = instance['collaborators']; + private_repos = instance['private_repos']; + } } - Map toJson(instance) => { - 'name': instance.name, - 'space': instance.space, - 'collaborators': instance.collaborators, - 'private_repos': instance.private_repos, + Map toJson() => { + 'name': name, + 'space': space, + 'collaborators': collaborators, + 'private_repos': private_repos, }; } diff --git a/lib/widget/common/assetbundle/demo.dart b/lib/widget/common/assetbundle/demo.dart index 540aa1e..af5f78b 100644 --- a/lib/widget/common/assetbundle/demo.dart +++ b/lib/widget/common/assetbundle/demo.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; class Index extends StatefulWidget { @override @@ -6,19 +7,33 @@ class Index extends StatefulWidget { } class _IndexState extends State { + String value = '加载中'; @override void initState() { super.initState(); + loadLocaleText(); + } + + loadLocaleText() async { + + String text = await rootBundle.loadString('locale/zh.json'); + print('text $text'); + setState(() { + value = text; + }); } @override Widget build(BuildContext context) { + print('bulid'); return Scaffold( appBar: AppBar( title: Text('AssetBundle'), ), - body: Center( - child: Text('更新中'), + body: SingleChildScrollView( + child: Center( + child: Text(value), + ), ), ); } From 1e062e50c4a18c05e46928321b4dae456038ceb3 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Mon, 8 Apr 2019 17:55:55 +0800 Subject: [PATCH 064/100] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/app_login/index.dart | 4 +- lib/store/objects/github_resp_info.dart | 40 +++++------ lib/store/objects/user_info.dart | 9 ++- lib/widget/common/iconbutton/demo.dart | 91 ++++++++++++------------- 4 files changed, 72 insertions(+), 72 deletions(-) diff --git a/lib/page/app_login/index.dart b/lib/page/app_login/index.dart index 9a8c516..638ebbb 100644 --- a/lib/page/app_login/index.dart +++ b/lib/page/app_login/index.dart @@ -134,8 +134,8 @@ class _IndexState extends State { .validate()) { await Store.value(context) .$loginController(context, { - 'name': nameCtl.text, - 'pwd': pwdCtl.text + 'name': nameCtl.text.trim(), + 'pwd': pwdCtl.text.trim() }); } }, diff --git a/lib/store/objects/github_resp_info.dart b/lib/store/objects/github_resp_info.dart index 01e60c2..15f4cd1 100644 --- a/lib/store/objects/github_resp_info.dart +++ b/lib/store/objects/github_resp_info.dart @@ -4,7 +4,7 @@ class GitHubRespInfo extends Object { num id; String url; - Map app; + App app; String token; String hashed_token; String token_last_eight; @@ -45,19 +45,19 @@ class GitHubRespInfo extends Object { fingerprint = json['fingerprint']; } - Map toJson(instance) => { - 'id': instance.id, - 'url': instance.url, - 'app': App.fromJson(instance.app), - 'token': instance.token, - 'hashed_token': instance.hashed_token, - 'token_last_eight': instance.token_last_eight, - 'note': instance.note, - 'note_url': instance.note_url, - 'created_at': instance.created_at, - 'updated_at': instance.updated_at, - 'scopes': instance.scopes, - 'fingerprint': instance.fingerprint, + Map toJson() => { + 'id': id, + 'url': url, + 'app': App.fromJson(app), + 'token': token, + 'hashed_token': hashed_token, + 'token_last_eight': token_last_eight, + 'note': note, + 'note_url': note_url, + 'created_at': created_at, + 'updated_at': updated_at, + 'scopes': scopes, + 'fingerprint': fingerprint, }; } @@ -84,11 +84,11 @@ class App extends Object { map = json['map']; } - Map toJson(instance) => { - 'str': instance.str, - 'number': instance.number, - 'boolean': instance.boolean, - 'array': instance.array, - 'map': instance.map, + Map toJson() => { + 'str': str, + 'number': number, + 'boolean': boolean, + 'array': array, + 'map': map, }; } diff --git a/lib/store/objects/user_info.dart b/lib/store/objects/user_info.dart index 6382da0..d2981be 100644 --- a/lib/store/objects/user_info.dart +++ b/lib/store/objects/user_info.dart @@ -119,7 +119,7 @@ class UserInfo extends Object { disk_usage = instance['disk_usage']; collaborators = instance['collaborators']; two_factor_authentication = instance['two_factor_authentication']; - plan = Plan.fromJson(json.decode(instance['plan'])); + plan = Plan.fromJson(instance['plan']); } Map toJson() => { @@ -160,7 +160,7 @@ class UserInfo extends Object { 'disk_usage': disk_usage, 'collaborators': collaborators, 'two_factor_authentication': two_factor_authentication, - 'plan': json.encode(plan), + 'plan': plan, }; } @@ -177,8 +177,11 @@ class Plan extends Object { this.private_repos, }); - Plan.fromJson(Map instance) { + Plan.fromJson(instance) { if (instance != null) { + if (instance.runtimeType == String) { + instance = json.decode(instance); + } name = instance['name']; space = instance['space']; collaborators = instance['collaborators']; diff --git a/lib/widget/common/iconbutton/demo.dart b/lib/widget/common/iconbutton/demo.dart index 477a135..e2a9e12 100644 --- a/lib/widget/common/iconbutton/demo.dart +++ b/lib/widget/common/iconbutton/demo.dart @@ -6,7 +6,7 @@ class Index extends StatefulWidget { } class _IndexState extends State { - List data = [58273,58275,59493,57903,58283,58284,57771,58285]; + List data = [58273, 58275, 59493, 57903, 58283, 58284, 57771, 58285]; int index = 0; @override void initState() { @@ -16,52 +16,49 @@ class _IndexState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text('IconButton'), - ), - body: Container( - padding: EdgeInsets.fromLTRB(20.0, 0, 20.0, 0), - child: ListView( - children: [ - IconButton( - iconSize: 30.0, - padding: EdgeInsets.all(8.0), - alignment: Alignment.center, - icon: Icon(IconData( - data[index], - fontFamily: 'MaterialIcons', - matchTextDirection: true - )), - color: Theme.of(context).primaryColor, - highlightColor: Colors.black12, - splashColor: Theme.of(context).primaryColorLight, - onPressed: (){ - if (index < 7) { - this.setState(() { - index = index + 1; - }); - } else { - this.setState(() { - index = 0; - }); - } - }, - tooltip: '长按文本提示', - ), - IconButton( - iconSize: 30.0, - padding: EdgeInsets.all(8.0), - alignment: Alignment.center, - icon: Icon(Icons.sync_disabled), - color: Theme.of(context).primaryColor, - highlightColor: Colors.black12, - splashColor: Theme.of(context).primaryColorLight, - tooltip: '长按文本提示', - disabledColor: Colors.grey - ) - ], + appBar: AppBar( + title: Text('IconButton'), ), - ) - ); + body: Container( + padding: EdgeInsets.fromLTRB(20.0, 0, 20.0, 0), + child: ListView( + children: [ + IconButton( + iconSize: 30.0, + padding: EdgeInsets.all(8.0), + alignment: Alignment.center, + icon: Icon(IconData(data[index], + fontFamily: 'MaterialIcons', matchTextDirection: true)), + color: Theme.of(context).primaryColor, + highlightColor: Colors.black12, + splashColor: Theme.of(context).primaryColorLight, + onPressed: () { + if (index < 7) { + this.setState(() { + index = index + 1; + }); + } else { + this.setState(() { + index = 0; + }); + } + }, + tooltip: '长按文本提示', + ), + IconButton( + iconSize: 30.0, + padding: EdgeInsets.all(8.0), + alignment: Alignment.center, + icon: Icon(Icons.sync_disabled), + color: Theme.of(context).primaryColor, + highlightColor: Colors.black12, + splashColor: Theme.of(context).primaryColorLight, + tooltip: '长按文本提示', + disabledColor: Colors.grey, + onPressed: () {}, + ) + ], + ), + )); } } From 46fbd4ed658ec188c75e090df241544a3ddcde0c Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Mon, 8 Apr 2019 18:58:36 +0800 Subject: [PATCH 065/100] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E7=99=BB?= =?UTF-8?q?=E5=BD=95loading=E7=AA=97=E5=8F=A3=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/controller/index.dart | 2 +- lib/http/loading.dart | 14 +++++--- lib/page/app_login/index.dart | 2 +- lib/page/home.dart | 2 +- lib/store/models/user_model.dart | 43 ++++++++++++------------- lib/store/objects/github_resp_info.dart | 4 +-- lib/store/objects/user_info.dart | 3 -- 7 files changed, 36 insertions(+), 34 deletions(-) diff --git a/lib/controller/index.dart b/lib/controller/index.dart index 2e839a1..c3b0693 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -7,7 +7,7 @@ void initState() { // 获取版本号 Store.valueNotCtx().$getAppVersion(); // 登录 - Store.valueNotCtx().$getLocalUserInfo(); + Store.valueNotCtx().getLocalUserInfo(); Future.delayed(Duration(seconds: 3), () { AppVersion().check(Store.widgetCtx); }); diff --git a/lib/http/loading.dart b/lib/http/loading.dart index 87ea73b..55e8074 100644 --- a/lib/http/loading.dart +++ b/lib/http/loading.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/store/index.dart' show Store; + bool loading = false; Set dict = Set(); @@ -46,6 +47,11 @@ class LoadingDialog extends StatefulWidget { } class LoadingDialogState extends State { + + @override + void initState() { + super.initState(); + } @override Widget build(BuildContext context) { return Scaffold( @@ -78,9 +84,9 @@ class LoadingDialogState extends State { } @override - void dispose() { - super.dispose(); - loading = false; - dict.clear(); + void dispose() async { + await super.dispose(); + // loading = false; + // await dict.clear(); } } diff --git a/lib/page/app_login/index.dart b/lib/page/app_login/index.dart index 638ebbb..b6e58fb 100644 --- a/lib/page/app_login/index.dart +++ b/lib/page/app_login/index.dart @@ -133,7 +133,7 @@ class _IndexState extends State { if ((_formKey.currentState as FormState) .validate()) { await Store.value(context) - .$loginController(context, { + .loginController(context, { 'name': nameCtl.text.trim(), 'pwd': pwdCtl.text.trim() }); diff --git a/lib/page/home.dart b/lib/page/home.dart index d323ae6..d321a1c 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -54,7 +54,7 @@ class _IndexState extends State { leading: Icon(Icons.exit_to_app), title: Text(AppLocalizations.$t('common.logout')), onTap: () { - Store.value(context).$clearUserInfo(); + Store.value(context).clearUserInfo(); }, ), ]; diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index e017b36..caf9102 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -6,24 +6,23 @@ import '../objects/user_info.dart' show UserInfo; import '../objects/github_resp_info.dart' show GitHubRespInfo; import 'package:efox_flutter/http/index.dart' as Http; import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; -import 'package:efox_flutter/http/loading.dart' as Loading; class UserModel with ChangeNotifier { UserInfo user = UserInfo(); Future testLogin() async { - await Loading.beforeRequest('aaa', {}); - return Future.delayed(Duration(seconds: 3), () async { - await Loading.afterResponse('aaa', {}); - print("返回中"); + return await Http.post(url: 'http://www.baidu.com').then((res) { return true; + }).catchError((error) async { + return await getUserInfo(); }); } /** * 登录控制 */ - Future $loginController(context, payload) async { - dynamic result = await $login(payload); + Future loginController(context, payload) async { + dynamic result = await login(payload); + // dynamic result = await testLogin(); print('返回result $result'); if (result == true) { print('登录成功后退'); @@ -36,7 +35,7 @@ class UserModel with ChangeNotifier { } } - Future $login(payload) async { + Future login(payload) async { var name = payload['name']; var pwd = payload['pwd']; var bytes = utf8.encode("$name:$pwd"); @@ -54,56 +53,56 @@ class UserModel with ChangeNotifier { options: options, ); return await response.then((resp) async { - await $setLoginRespInfo(resp.data); - return true; + return await setLoginRespInfo(resp.data); }).catchError((error) { - $clearUserInfo(); + clearUserInfo(); return false; }); } - $setLoginRespInfo(payload) async { + setLoginRespInfo(payload) async { GitHubRespInfo user = GitHubRespInfo.fromJson(payload); LocalStorage.set('githubRespInfo', user.toString()); print('user.token.toString() ${user.token.toString()}'); LocalStorage.set('githubRespLoginToken', user.token.toString()); - await $getUserInfo(); // 授权成功获取用户信息 + return await getUserInfo(); // 授权成功获取用户信息 } /** * 授权成功或打开app时获取用户信息 */ - Future $getUserInfo() async { + Future getUserInfo() async { var response = Http.post( url: 'https://api.github.com/user', ); - await response.then((resp) { + return await response.then((resp) { UserInfo user = UserInfo.fromJson(resp.data); - $setUserInfo(user); + setUserInfo(user); + return true; }).catchError((error) { print('ERROR $error'); - // $clearUserInfo(); + return false; }); } /** * 获取本地数据,减少调用接口 */ - $getLocalUserInfo() async { + getLocalUserInfo() async { String data = await LocalStorage.get('githubUserInfo'); print("本地数据 $data"); if (data == null) { - $getUserInfo(); + getUserInfo(); return; } UserInfo user = UserInfo.fromJson(json.decode(data)); - $setUserInfo(user); + setUserInfo(user); } /** * 设置用户信息 */ - $setUserInfo(payload) { + setUserInfo(payload) { user = payload; if (user != null && user.id != null) { LocalStorage.set('githubUserInfo', json.encode(user)); @@ -114,7 +113,7 @@ class UserModel with ChangeNotifier { /** * 清空用户信息 */ - $clearUserInfo() { + clearUserInfo() { user = UserInfo(); LocalStorage.remove('githubUserInfo'); LocalStorage.remove('githubRespInfo'); diff --git a/lib/store/objects/github_resp_info.dart b/lib/store/objects/github_resp_info.dart index 15f4cd1..21bca5e 100644 --- a/lib/store/objects/github_resp_info.dart +++ b/lib/store/objects/github_resp_info.dart @@ -33,7 +33,7 @@ class GitHubRespInfo extends Object { GitHubRespInfo.fromJson(json) { id = json['id']; url = json['url']; - app = json['app']; + app = App.fromJson(json['app']); token = json['token']; hashed_token = json['hashed_token']; token_last_eight = json['token_last_eight']; @@ -48,7 +48,7 @@ class GitHubRespInfo extends Object { Map toJson() => { 'id': id, 'url': url, - 'app': App.fromJson(app), + 'app': app, 'token': token, 'hashed_token': hashed_token, 'token_last_eight': token_last_eight, diff --git a/lib/store/objects/user_info.dart b/lib/store/objects/user_info.dart index d2981be..e5bdcbd 100644 --- a/lib/store/objects/user_info.dart +++ b/lib/store/objects/user_info.dart @@ -179,9 +179,6 @@ class Plan extends Object { Plan.fromJson(instance) { if (instance != null) { - if (instance.runtimeType == String) { - instance = json.decode(instance); - } name = instance['name']; space = instance['space']; collaborators = instance['collaborators']; From 30017f7038e1e254de60051699d0d020d02d8a0b Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 9 Apr 2019 19:09:44 +0800 Subject: [PATCH 066/100] feat: RaisedButton --- docs/widget/common/raisedbutton/index.md | 58 ++++++++++++- lib/widget/common/raisedbutton/demo.dart | 100 ++++++++++++++++++++++- readme/widget_progress.md | 2 +- 3 files changed, 157 insertions(+), 3 deletions(-) diff --git a/docs/widget/common/raisedbutton/index.md b/docs/widget/common/raisedbutton/index.md index 83532f6..012b1cd 100644 --- a/docs/widget/common/raisedbutton/index.md +++ b/docs/widget/common/raisedbutton/index.md @@ -1 +1,57 @@ -## **文档完善中** \ No newline at end of file +## **RaisedButton** +> +RaisedButton 凸起按钮 + +### 构造方法 +``` dart +RaisedButton({ + Key key, + @required VoidCallback onPressed, + ValueChanged onHighlightChanged, + ButtonTextTheme textTheme, + Color textColor, + Color disabledTextColor, + Color color, + Color disabledColor, + Color highlightColor, + Color splashColor, + Brightness colorBrightness, + double elevation, + double highlightElevation, + double disabledElevation, + EdgeInsetsGeometry padding, + ShapeBorder shape, + Clip clipBehavior = Clip.none, + MaterialTapTargetSize materialTapTargetSize, + Duration animationDuration, + Widget child, + }) +``` + +### 属性介绍 +* onPressed: 点击回调 +* textColor:文本颜色 +* disabledTextColor:禁用文本颜色 +* color:按钮颜色 +* disabledColor: 禁用按钮颜色 +* highlightColor:长按按钮颜色 +* splashColor: 点击按钮水波纹颜色 +* elevation: 按钮下面阴影 +* highlightElevation:阴影大小 +* disabledElevation:禁止阴影 +* padding:按钮填充区域 +* shape:按钮形状 +* child:按钮中的Widget + +### 高级用法 +按钮形状 +* BeveledRectangleBorder 带斜角的长方形边框 +* CircleBorder 圆形边框 +* RoundedRectangleBorder 圆角矩形 +* StadiumBorder 两端是半圆的边框 + +设置按钮形状常用属性 +* side:用来设置边线(颜色、宽度等) +* borderRadius:用来设置圆角 + +使用RaisedButton.icon可以创建图标(icon)和标签(label)的widget文本按钮 \ No newline at end of file diff --git a/lib/widget/common/raisedbutton/demo.dart b/lib/widget/common/raisedbutton/demo.dart index 648eda4..4d412b5 100644 --- a/lib/widget/common/raisedbutton/demo.dart +++ b/lib/widget/common/raisedbutton/demo.dart @@ -6,6 +6,7 @@ class Index extends StatefulWidget { } class _IndexState extends State { + var params = 0.0; @override void initState() { super.initState(); @@ -18,7 +19,104 @@ class _IndexState extends State { title: Text('RaisedButton'), ), body: Center( - child: Text('更新中'), + child: Wrap( + alignment: WrapAlignment.spaceEvenly, + children: [ + RaisedButton( + onPressed: () { + setState(() { + params == 0.0 ? params = 10.0 : params = 0.0; + }); + }, + child: Text('BeveledRectangleBorder'), + textColor: Colors.white, + textTheme: ButtonTextTheme.normal, + color: Theme.of(context).primaryColor, + splashColor: Colors.pink, + highlightColor: Colors.pink, + elevation: 2.0, + highlightElevation: 8.0, + padding: EdgeInsets.all(10.0), + shape: BeveledRectangleBorder( + side: BorderSide( + color: Colors.green + ), + borderRadius: BorderRadius.all(Radius.circular(params)) + ) + ), + RaisedButton( + onPressed: () { + setState(() { + params == 0.0 ? params = 10.0 : params = 0.0; + }); + }, + child: Text('RoundedRectangleBorder'), + textColor: Colors.white, + textTheme: ButtonTextTheme.normal, + color: Theme.of(context).primaryColor, + splashColor: Colors.pink, + highlightColor: Colors.pink, + elevation: 2.0, + highlightElevation: 8.0, + padding: EdgeInsets.all(10.0), + shape: RoundedRectangleBorder( + side: BorderSide( + color: Colors.green + ), + borderRadius: BorderRadius.circular(params) + ) + ), + RaisedButton( + onPressed: () { + setState(() { + params == 0.0 ? params = 10.0 : params = 0.0; + }); + }, + child: Text('CircleBorder'), + textColor: Colors.white, + textTheme: ButtonTextTheme.normal, + color: Theme.of(context).primaryColor, + splashColor: Colors.pink, + highlightColor: Colors.pink, + elevation: 2.0, + highlightElevation: 8.0, + padding: EdgeInsets.all(30.0), + shape: CircleBorder( + side: BorderSide( + color: Colors.green + ), + ) + ), + RaisedButton( + onPressed: () { + setState(() { + params == 0.0 ? params = 10.0 : params = 0.0; + }); + }, + child: Text('StadiumBorder'), + textColor: Colors.white, + textTheme: ButtonTextTheme.normal, + color: Theme.of(context).primaryColor, + splashColor: Colors.pink, + highlightColor: Colors.pink, + elevation: 2.0, + highlightElevation: 8.0, + padding: EdgeInsets.all(10.0), + shape: StadiumBorder( + side: BorderSide( + color: Colors.green + ) + ) + ), + RaisedButton.icon( + icon: Icon(Icons.android, size: 28.0,), + label: Text('RaisedButton.icon'), + onPressed: () {}, + textColor: Colors.white, + color: Theme.of(context).primaryColor, + ) + ], + ), ), ); } diff --git a/readme/widget_progress.md b/readme/widget_progress.md index e37b610..03ca961 100644 --- a/readme/widget_progress.md +++ b/readme/widget_progress.md @@ -58,7 +58,7 @@ Flutter UI │ ├─image 【✔️ v1.0】 │ ├─listtile 【✔️ v1.0】 │ ├─placeholder - │ ├─raisedbutton + │ ├─raisedbutton 【✔️ v1.0】 │ ├─rawimage │ ├─stepper 【✔️ v1.0】 │ ├─text 【✔️ v1.0】 From ee0aa881775f7ae48eb096b11484c6813af7d277 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 11 Apr 2019 22:14:16 +0800 Subject: [PATCH 067/100] feat: chip --- docs/widget/common/chip/index.md | 6 ++ lib/widget/common/chip/demo.dart | 165 ++++++++++++++++++++++++++++++- 2 files changed, 169 insertions(+), 2 deletions(-) diff --git a/docs/widget/common/chip/index.md b/docs/widget/common/chip/index.md index 83532f6..7456f7c 100644 --- a/docs/widget/common/chip/index.md +++ b/docs/widget/common/chip/index.md @@ -1 +1,7 @@ +## **文档完善中** + +## **文档完善中** +## **文档完善中** +## **文档完善中** +## **文档完善中** ## **文档完善中** \ No newline at end of file diff --git a/lib/widget/common/chip/demo.dart b/lib/widget/common/chip/demo.dart index fffa07d..7a26125 100644 --- a/lib/widget/common/chip/demo.dart +++ b/lib/widget/common/chip/demo.dart @@ -6,6 +6,15 @@ class Index extends StatefulWidget { } class _IndexState extends State { + List _tags = [ + 'cake', + 'chicken', + 'apple' + ]; + String _action = '点击下面'; + List _selected = []; + String _choice = '点击下面'; + @override void initState() { super.initState(); @@ -17,9 +26,161 @@ class _IndexState extends State { appBar: AppBar( title: Text('Chip'), ), - body: Center( - child: Text('更新中'), + body: Container( + padding: EdgeInsets.all(16.0), + child: ListView( + children: [ + Container( + child: Text('chip:'), + ), + ChipDemo(), + Divider(), + ChipDemoDelete(), + Divider(), + Container( + child: Text('ActionChip: $_action'), + ), + ActionChipDemo(), + Divider(), + Container( + child: Text('FilterChip: $_selected'), + ), + FilterChipDemo(), + Divider(), + Container( + child: Text('ChoiceChip: $_choice'), + ), + ChoiceChipDemo() + ], + ), + ), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.restore), + backgroundColor: Theme.of(context).primaryColor, + onPressed: (){ + setState(() { + _tags = [ + 'cake', + 'chicken', + 'apple' + ]; + _selected = []; + _action = '点击下面'; + _choice = '点击下面'; + }); + }, ), ); } + + Widget ChipDemo() { + return Wrap( + spacing: 20.0, + runSpacing: 8.0, + children: [ + Chip( + label: Text('Efox'), + ), + Chip( + label: Text('backgroundColor'), + backgroundColor: Theme.of(context).primaryColor, + labelStyle: TextStyle(color: Colors.white), + // labelPadding: EdgeInsets.all(2.0), + ), + Chip( + label: Text('Efox Team'), + avatar: CircleAvatar( + backgroundColor: Theme.of(context).primaryColor, + child: Text('E'), + ), + ), + Chip( + label: Text('Efox Team'), + avatar: CircleAvatar( + backgroundImage: NetworkImage('http://pic1.win4000.com/wallpaper/2019-04-02/5ca2ccd945964.jpg'), + ), + ), + Chip( + label: Text('City'), + onDeleted: (){}, + deleteIcon: Icon(Icons.delete, size: 26.0,), + deleteIconColor: Theme.of(context).primaryColor, + deleteButtonTooltipMessage: 'Remove this tag', + ) + ], + ); + } + + Widget ChipDemoDelete () { + return Wrap( + spacing: 8.0, + children: _tags.map((tag){ + return Chip( + label: Text(tag), + onDeleted: () { + setState(() { + _tags.remove(tag); + }); + }, + ); + }).toList(), + ); + } + + Widget ActionChipDemo () { + return Wrap( + spacing: 8.0, + children: _tags.map((tag){ + return ActionChip( + label: Text(tag), + onPressed: (){ + setState(() { + _action = tag; + }); + }, + ); + }).toList(), + ); + } + + Widget FilterChipDemo(){ + return Wrap( + spacing: 8.0, + children: _tags.map((tag){ + return FilterChip( + label: Text(tag), + selected: _selected.contains(tag), + selectedColor: Theme.of(context).primaryColor, + onSelected: (value) { + setState(() { + if (_selected.contains(tag)) { + _selected.remove(tag); + } else { + _selected.add(tag); + } + }); + }, + ); + }).toList(), + ); + } + + Widget ChoiceChipDemo () { + return Wrap( + spacing: 8.0, + children: _tags.map((tag) { + return ChoiceChip( + label: Text(tag), + selectedColor: Theme.of(context).primaryColor, + labelStyle: TextStyle(color: Colors.white), + selected: _choice == tag, + onSelected: (value) { + setState(() { + _choice = tag; + }); + }, + ); + }).toList(), + ); + } } From f8e4d9615868b228ffdac94e10794f87c1a6883f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E5=8D=97?= Date: Sat, 13 Apr 2019 11:21:45 +0800 Subject: [PATCH 068/100] =?UTF-8?q?fix:=20=E7=BB=84=E4=BB=B6=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E9=A1=B6=E9=83=A8=E7=8A=B6=E6=80=81=E6=9D=A1?= =?UTF-8?q?=E9=A2=9C=E8=89=B2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/components/widget_comp.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/components/widget_comp.dart b/lib/components/widget_comp.dart index 0230c94..402f313 100644 --- a/lib/components/widget_comp.dart +++ b/lib/components/widget_comp.dart @@ -106,6 +106,7 @@ class IndexState extends State { actions: this.getActions( context, ), + brightness: Brightness.light, leading: IconButton( icon: Icon(Icons.arrow_back), color: Color(AppTheme.blackColor), From 57c6336941dac434bf6ab1a975475555d281c722 Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 15 Apr 2019 21:07:05 +0800 Subject: [PATCH 069/100] feat: chip --- docs/widget/common/chip/index.md | 51 ++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/docs/widget/common/chip/index.md b/docs/widget/common/chip/index.md index 7456f7c..95e49c0 100644 --- a/docs/widget/common/chip/index.md +++ b/docs/widget/common/chip/index.md @@ -1,7 +1,46 @@ -## **文档完善中** +## **Chip** -## **文档完善中** -## **文档完善中** -## **文档完善中** -## **文档完善中** -## **文档完善中** \ No newline at end of file +> +一般用于显示标签的控件 + +### 构造方法 +``` dart +Chip({ + Key key, + this.avatar, + @required this.label, + this.labelStyle, + this.labelPadding, + this.deleteIcon, + this.onDeleted, + this.deleteIconColor, + this.deleteButtonTooltipMessage, + this.shape, + this.clipBehavior = Clip.none, + this.backgroundColor, + this.padding, + this.materialTapTargetSize, + this.elevation, + }) +``` + +### 属性介绍 + +* avatar:标签左侧widget,一般为小图标 +* label:标签内容 +* labelStyle:标签样式 +* labelPadding:标签周围填充 +* deleteIcon:删除图标 +* onDeleted:删除回调,为空时不显示删除图标 +* deleteIconColor:删除图标颜色 +* deleteButtonTooltipMessage:删除图标的tip提示文字 +* shape:形状 +* backgroundColor:背景颜色 +* padding:标签内容和外形之间的填充 +* materialTapTargetSize:配置点击目标的最小尺寸 +* elevation:标签阴影 + +### 延生 +* ActionChip +* FilterChip +* ChoiceChip From 4b0863d413ec927eadc99548d2dd116c5fae0d81 Mon Sep 17 00:00:00 2001 From: linhaoran <1476589247@qq.com> Date: Wed, 17 Apr 2019 20:39:51 +0800 Subject: [PATCH 070/100] =?UTF-8?q?feat:=E7=8E=AF=E5=A2=83=E5=88=87?= =?UTF-8?q?=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/components/widget_comp.dart | 32 ++++++++++++++++++++++++ lib/page/mine/index.dart | 12 +++++++++ lib/store/models/config_state_model.dart | 5 ++++ 3 files changed, 49 insertions(+) diff --git a/lib/components/widget_comp.dart b/lib/components/widget_comp.dart index 0230c94..81271b5 100644 --- a/lib/components/widget_comp.dart +++ b/lib/components/widget_comp.dart @@ -116,6 +116,29 @@ class IndexState extends State { ); } + showCode(context) async { + if (Store.value(context).isPro) { + // 线上文档 + FluroRouter.router.navigateTo( + context, + 'webview?title=${widget.title}&url=${Uri.encodeComponent(Store.value(context).env.githubAssetOrigin+widget.mdUrl)}' + ); + } else { + // 加载本地 + String mdStr = await AssetUtils.readLocaleFile(widget.mdUrl); + Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext build) { + return Scaffold( + appBar: AppBar(title: Text(widget.title),), + body: ListView( + children: [markdown_comp.Index(mdStr)], + ), + ); + }) + ); + } + } + Future getMdFile(url) async { // bool productionEnv = Store.value(context).isPro; bool productionEnv = false; @@ -128,6 +151,15 @@ class IndexState extends State { getActions(context) { return [ + IconButton( + color: Color(AppTheme.blackColor), + icon: Icon( + Icons.code + ), + onPressed: () async { + this.showCode(context); + }, + ), IconButton( color: Color(AppTheme.blackColor), icon: Icon( diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index cc14bf4..e270f25 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -111,6 +111,18 @@ class _IndexState extends State { Divider( color: Color(AppTheme.lineColor), ), + ListTile( + onTap: () { + Store.value(context).$setIsPro(); + }, + leading: Icon(Icons.verified_user), + title: Text( + Store.value(context).isPro ? '线上环境' : '本地环境' + ), + ), + Divider( + color: Color(AppTheme.lineColor), + ), (Platform.isAndroid) ? ListTile( onTap: () { diff --git a/lib/store/models/config_state_model.dart b/lib/store/models/config_state_model.dart index 6f9913e..3edcdbf 100644 --- a/lib/store/models/config_state_model.dart +++ b/lib/store/models/config_state_model.dart @@ -32,4 +32,9 @@ class ConfigModel extends ConfigInfo with ChangeNotifier { notifyListeners(); } + Future $setIsPro() async { + isPro = !isPro; + notifyListeners(); + } + } From f75e1a3e279b6ef6717c2690b99d077a58f71efb Mon Sep 17 00:00:00 2001 From: KenZR Date: Fri, 19 Apr 2019 19:06:26 +0800 Subject: [PATCH 071/100] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E5=A4=9A?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E9=97=AE=E9=A2=98=EF=BC=8C=E6=B2=A1=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E8=AF=B7=E6=B1=82=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/controller/index.dart | 2 +- lib/http/index.dart | 13 +++++++------ lib/page/mine/index.dart | 6 +++--- lib/store/objects/user_info.dart | 2 +- locale/en.json | 4 +++- locale/zh.json | 4 +++- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/controller/index.dart b/lib/controller/index.dart index c3b0693..1bf3d8e 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -7,7 +7,7 @@ void initState() { // 获取版本号 Store.valueNotCtx().$getAppVersion(); // 登录 - Store.valueNotCtx().getLocalUserInfo(); + //Store.valueNotCtx().getLocalUserInfo(); Future.delayed(Duration(seconds: 3), () { AppVersion().check(Store.widgetCtx); }); diff --git a/lib/http/index.dart b/lib/http/index.dart index 6e0dbba..68d79f3 100644 --- a/lib/http/index.dart +++ b/lib/http/index.dart @@ -47,10 +47,12 @@ Dio getDio({options, loading}) { print("返回值 ${response.data}"); print('=========【请求成功】End============'); await AppLoading.afterResponse(response.request.uri, loading); - return response; + return response; }, onError: (DioError e) async { - await AppLoading.afterResponse(e.request.uri, loading); + print('e.request.uri=======================${e.request}'); + if (e.request != null && e.request.uri != null) + await AppLoading.afterResponse(e.request.uri, loading); dynamic msg = e.message; dynamic code = 0; // 错误码 dynamic status = e.type; // http请求状态 @@ -60,7 +62,7 @@ Dio getDio({options, loading}) { status = e.response.statusCode; } print('========【请求失败 Start】============='); - print("请求地址 ${e.request.uri}"); + print("请求地址 ${e.request}"); print("状态码 ${status}"); print("返回msg ${msg}"); print('=========【请求失败 End】============'); @@ -68,13 +70,12 @@ Dio getDio({options, loading}) { }, )); dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志 - + return dio; } Future get({url, data = const {}, options, loading}) async { - return getDio(options: options, loading: loading ?? Map()) - .get(url); + return getDio(options: options, loading: loading ?? Map()).get(url); } Future post({url, data = const {}, options, loading}) async { diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index e270f25..dadefe8 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -116,9 +116,9 @@ class _IndexState extends State { Store.value(context).$setIsPro(); }, leading: Icon(Icons.verified_user), - title: Text( - Store.value(context).isPro ? '线上环境' : '本地环境' - ), + title: Text(Store.value(context).isPro + ? AppLocalizations.$t('common_mine_1.doc_online') + : AppLocalizations.$t('common_mine_1.doc_offline')), ), Divider( color: Color(AppTheme.lineColor), diff --git a/lib/store/objects/user_info.dart b/lib/store/objects/user_info.dart index e5bdcbd..7c6af85 100644 --- a/lib/store/objects/user_info.dart +++ b/lib/store/objects/user_info.dart @@ -1,4 +1,4 @@ -import 'dart:convert'; +//import 'dart:convert'; class UserInfo extends Object { String login; diff --git a/locale/en.json b/locale/en.json index d65bb5e..8702fc7 100644 --- a/locale/en.json +++ b/locale/en.json @@ -13,7 +13,9 @@ "compProgress": "Components Progress", "success": "Success To Change ", "theme": "Select Theme", - "version": "Version" + "version": "Version", + "doc_online": "document online", + "doc_offline": "document offline" }, "mine": { "loadNetwork": "Load Network Document Resources", diff --git a/locale/zh.json b/locale/zh.json index 6c22b44..f1dd90f 100644 --- a/locale/zh.json +++ b/locale/zh.json @@ -13,7 +13,9 @@ "compProgress": "组件进度", "success": "切换成功", "theme": "选择主题", - "version": "版本" + "version": "版本", + "doc_online": "线上文档", + "doc_offline": "离线文档" }, "mine": { "loadNetwork": "网络优良,可选择加载线上文档资源", From 90aac3fdfabbfc2e93cff605406465de9ae40a33 Mon Sep 17 00:00:00 2001 From: wanwu <609780590@qq.com> Date: Fri, 26 Apr 2019 16:56:16 +0800 Subject: [PATCH 072/100] =?UTF-8?q?modify:=20=E5=A2=9E=E5=8A=A0md=E6=8E=A8?= =?UTF-8?q?=E5=B9=BF=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加md中推文内容 --- readme.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index 9a684f5..f7ea7bc 100644 --- a/readme.md +++ b/readme.md @@ -1,25 +1,27 @@ # Flutter UI v1.0.2 -> flutter 开发者社区 - - -## google play 下载 - - - git it on google play - +> flutter 开发者社区 ## 功能清单 + widget 组件教程 + 多语言切换 + 多主题切换 + 自动更新检测 + firebase 崩溃监控 + +## 推文 +[Flutter 全局弹窗](https://juejin.im/post/5c9f2c37518825609415d11d) +[Flutter UI使用Provide实现主题切换](https://juejin.im/post/5ca5e240f265da30c1725021) +[Flutter UI 1.0.2落地与优化小结](https://juejin.im/post/5c95e691f265da610c06905c) +[Flutter Provide实现](https://juejin.im/post/5c9f2c37518825609415d11d) ## apk 下载 ![安卓包下载](readme/apk.png) [安卓包下载](https://github.com/efoxTeam/flutter-ui/releases/download/v1.0.2/app-release.apk) +## Demo 预览 + + ## 项目相关 + [apk包历史版本](https://github.com/efoxTeam/flutter-ui/releases) From 17158f0dafe997dd08648fe1a4486ce5f8fccaed Mon Sep 17 00:00:00 2001 From: wanwu <609780590@qq.com> Date: Fri, 26 Apr 2019 16:56:51 +0800 Subject: [PATCH 073/100] =?UTF-8?q?modify=EF=BC=9A=20=E4=BF=AE=E6=94=B9md?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit modify: 修改md样式 --- readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index f7ea7bc..2deacfe 100644 --- a/readme.md +++ b/readme.md @@ -10,10 +10,10 @@ + firebase 崩溃监控 ## 推文 -[Flutter 全局弹窗](https://juejin.im/post/5c9f2c37518825609415d11d) -[Flutter UI使用Provide实现主题切换](https://juejin.im/post/5ca5e240f265da30c1725021) -[Flutter UI 1.0.2落地与优化小结](https://juejin.im/post/5c95e691f265da610c06905c) -[Flutter Provide实现](https://juejin.im/post/5c9f2c37518825609415d11d) ++ [Flutter 全局弹窗](https://juejin.im/post/5c9f2c37518825609415d11d) ++ [Flutter UI使用Provide实现主题切换](https://juejin.im/post/5ca5e240f265da30c1725021) ++ [Flutter UI 1.0.2落地与优化小结](https://juejin.im/post/5c95e691f265da610c06905c) ++ [Flutter Provide实现](https://juejin.im/post/5c9f2c37518825609415d11d) ## apk 下载 ![安卓包下载](readme/apk.png) From ce27fddcdd1fc68dafabcf85523f38ec1ba488fe Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Fri, 26 Apr 2019 16:57:28 +0800 Subject: [PATCH 074/100] =?UTF-8?q?modify:=E4=BF=AE=E6=94=B9md=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/components/widget_comp.dart | 2 +- lib/page/app_login/index.dart | 84 ++++++++++++++++++++++++++++++++- lib/page/app_login/text.dart | 1 + lib/page/home.dart | 16 +++++++ readme.md | 6 +++ 5 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 lib/page/app_login/text.dart diff --git a/lib/components/widget_comp.dart b/lib/components/widget_comp.dart index 81271b5..664857e 100644 --- a/lib/components/widget_comp.dart +++ b/lib/components/widget_comp.dart @@ -50,7 +50,7 @@ class IndexState extends State { backgroundImage: NetworkImage( info.avatarUrl, ), - radius: 35, + radius: 30, ), title: Text( info.name, diff --git a/lib/page/app_login/index.dart b/lib/page/app_login/index.dart index b6e58fb..847db3f 100644 --- a/lib/page/app_login/index.dart +++ b/lib/page/app_login/index.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'dart:math' as math; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/store/index.dart' show Store, UserModel; +import './text.dart' as Content; class Index extends StatefulWidget { Index({Key key}) : super(key: key); @@ -23,9 +24,87 @@ class _IndexState extends State { */ renderOverlay(String text) { _overlayEntry?.remove(); + Size _size = MediaQuery.of(context).size; _overlayState = Overlay.of(context); _overlayEntry = OverlayEntry(builder: (context) { - return Center(child: Text(text)); + return Scaffold( + backgroundColor: Colors.transparent, + body: GestureDetector( + child: Stack( + children: [ + Opacity( + opacity: 0.75, + child: Container( + decoration: BoxDecoration( + color: Colors.black, + ), + ), + ), + SizedBox.expand( + child: Center( + child: SizedBox( + height: _size.height / 1.3, + width: _size.width / 1.3, + child: Container( + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.grey, + blurRadius: 2, + // offset: Offset(0, 1), + ), + ], + ), + child: Column( + children: [ + Text( + "说明", + style: TextStyle( + color: Colors.black, + fontSize: 22, + ), + ), + SizedBox( + height: 10, + ), + Expanded( + child: SingleChildScrollView( + child: Text( + text, + textAlign: TextAlign.left, + style: TextStyle( + color: Colors.black, + fontSize: 16, + ), + ), + ), + ), + FlatButton( + textColor: Theme.of(context).primaryColor, + onPressed: () { + beforeDispose(); + }, + child: Padding( + padding: EdgeInsets.all(10), + child: Text("确定"), + ), + ), + ], + ), + ), + ), + ), + ), + ], + ), + onTap: () { + beforeDispose(); + }, + ), + ); }); _overlayState.insert(_overlayEntry); } @@ -160,7 +239,7 @@ class _IndexState extends State { ), ), onTap: () { - renderOverlay(" Text 1"); + renderOverlay(Content.loginText); }, ), SizedBox( @@ -193,6 +272,7 @@ class _IndexState extends State { }, ); } + beforeDispose() { if (_overlayEntry != null) { _overlayEntry.remove(); diff --git a/lib/page/app_login/text.dart b/lib/page/app_login/text.dart new file mode 100644 index 0000000..3ecb96a --- /dev/null +++ b/lib/page/app_login/text.dart @@ -0,0 +1 @@ +const loginText = 'a1sd56a1sd56as1d'; \ No newline at end of file diff --git a/lib/page/home.dart b/lib/page/home.dart index d321a1c..03d7d88 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -47,6 +47,9 @@ class _IndexState extends State { ); } + /** + * 左侧列表 + */ List renderTiles(id) { if (id != null) { return [ @@ -57,6 +60,19 @@ class _IndexState extends State { Store.value(context).clearUserInfo(); }, ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text(AppLocalizations.$t('common.login')), + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return LoginIndex.Index(); + }, + ), + ); + }, + ) ]; } return [ diff --git a/readme.md b/readme.md index f28ff36..2deacfe 100644 --- a/readme.md +++ b/readme.md @@ -8,6 +8,12 @@ + 多主题切换 + 自动更新检测 + firebase 崩溃监控 + +## 推文 ++ [Flutter 全局弹窗](https://juejin.im/post/5c9f2c37518825609415d11d) ++ [Flutter UI使用Provide实现主题切换](https://juejin.im/post/5ca5e240f265da30c1725021) ++ [Flutter UI 1.0.2落地与优化小结](https://juejin.im/post/5c95e691f265da610c06905c) ++ [Flutter Provide实现](https://juejin.im/post/5c9f2c37518825609415d11d) ## apk 下载 ![安卓包下载](readme/apk.png) From 71406e215022ac2301d43ed8f7e14060a00bf0a0 Mon Sep 17 00:00:00 2001 From: wanwu <609780590@qq.com> Date: Mon, 6 May 2019 10:14:42 +0800 Subject: [PATCH 075/100] modify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改地址 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 2deacfe..b0f4b52 100644 --- a/readme.md +++ b/readme.md @@ -13,7 +13,7 @@ + [Flutter 全局弹窗](https://juejin.im/post/5c9f2c37518825609415d11d) + [Flutter UI使用Provide实现主题切换](https://juejin.im/post/5ca5e240f265da30c1725021) + [Flutter UI 1.0.2落地与优化小结](https://juejin.im/post/5c95e691f265da610c06905c) -+ [Flutter Provide实现](https://juejin.im/post/5c9f2c37518825609415d11d) ++ [Flutter provide ProvideMulti](https://juejin.im/post/5cc685835188252e8925f056) ## apk 下载 ![安卓包下载](readme/apk.png) From 29d4a5ff970f39daa4862477bbf50a29d40eb328 Mon Sep 17 00:00:00 2001 From: linhaoran <1476589247@qq.com> Date: Wed, 5 Jun 2019 16:03:41 +0800 Subject: [PATCH 076/100] =?UTF-8?q?feat:=20=E6=8E=A5=E5=85=A5star?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/config/index.dart | 1 + lib/controller/index.dart | 9 +- lib/http/index.dart | 5 + lib/page/app_login/index.dart | 1 + lib/page/home.dart | 76 +++- lib/store/models/user_model.dart | 93 ++++- lib/store/objects/flutter_ui_info.dart | 492 +++++++++++++++++++++++++ 7 files changed, 656 insertions(+), 21 deletions(-) create mode 100644 lib/store/objects/flutter_ui_info.dart diff --git a/lib/config/index.dart b/lib/config/index.dart index 08e9168..787f047 100644 --- a/lib/config/index.dart +++ b/lib/config/index.dart @@ -2,5 +2,6 @@ import 'development.dart' as Development; import 'production.dart' as Production; const bool isPro = false; +const String owner_repo = 'efoxTeam/flutter-ui'; Object env = isPro ? Production.Config() : Development.Config(); diff --git a/lib/controller/index.dart b/lib/controller/index.dart index 1bf3d8e..3f68c27 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -3,11 +3,16 @@ import 'package:efox_flutter/store/index.dart' import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; -void initState() { +void initState() async { // 获取版本号 Store.valueNotCtx().$getAppVersion(); // 登录 - //Store.valueNotCtx().getLocalUserInfo(); + Store.valueNotCtx().getLocalUserInfo().then((res) { + if (res) { + Store.valueNotCtx().getUserStar(); + } + }); + Store.valueNotCtx().getFlutterUIStar(); Future.delayed(Duration(seconds: 3), () { AppVersion().check(Store.widgetCtx); }); diff --git a/lib/http/index.dart b/lib/http/index.dart index 68d79f3..9ae1631 100644 --- a/lib/http/index.dart +++ b/lib/http/index.dart @@ -82,3 +82,8 @@ Future post({url, data = const {}, options, loading}) async { return getDio(options: options, loading: loading ?? Map()) .post(url, data: data); } + +Future put({url, data = const {}, options, loading}) async { + return getDio(options: options, loading: loading ?? Map()) + .put(url, data: data); +} diff --git a/lib/page/app_login/index.dart b/lib/page/app_login/index.dart index b6e58fb..87f0391 100644 --- a/lib/page/app_login/index.dart +++ b/lib/page/app_login/index.dart @@ -137,6 +137,7 @@ class _IndexState extends State { 'name': nameCtl.text.trim(), 'pwd': pwdCtl.text.trim() }); + await Store.value(context).getUserStar(); } }, ), diff --git a/lib/page/home.dart b/lib/page/home.dart index d321a1c..8c52dff 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -30,20 +30,24 @@ class _IndexState extends State { } Widget _bottomNavigationBar() { - return BottomNavigationBar( - items: [ - BottomNavigationBarItem( - title: Text(AppLocalizations.$t('title_component')), - icon: Icon(Icons.dashboard)), - BottomNavigationBarItem( - title: Text(AppLocalizations.$t('title_my')), - icon: Icon(Icons.person_outline)), - ], - type: BottomNavigationBarType.fixed, - currentIndex: _currentIndex, - onTap: (int index) { - _pageController.jumpToPage(index); - }, + return BottomAppBar( + shape: CircularNotchedRectangle(), + clipBehavior: Clip.antiAlias, + child: BottomNavigationBar( + items: [ + BottomNavigationBarItem( + title: Text(AppLocalizations.$t('title_component')), + icon: Icon(Icons.dashboard)), + BottomNavigationBarItem( + title: Text(AppLocalizations.$t('title_my')), + icon: Icon(Icons.person_outline)), + ], + // type: BottomNavigationBarType.fixed, + currentIndex: _currentIndex, + onTap: (int index) { + _pageController.jumpToPage(index); + }, + ), ); } @@ -55,6 +59,7 @@ class _IndexState extends State { title: Text(AppLocalizations.$t('common.logout')), onTap: () { Store.value(context).clearUserInfo(); + Store.value(context).changeIsStar(false); }, ), ]; @@ -122,12 +127,55 @@ class _IndexState extends State { ); } + Widget _floatingActionButton() { + return Store.connect( + builder: (context, child, model) { + return FloatingActionButton( + backgroundColor: Theme.of(context).primaryColor, + onPressed: () { + if(!model.isStar&&model.user.id != null) { + print('进行star'); + model.setStarFlutterUI(); + } else { + print('不满足进行star条件'); + if(model.user.id == null) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return LoginIndex.Index(); + } + ) + ); + } + } + }, + child: Container( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + model.isStar + ?Icon(Icons.star,size: 20, color: Colors.white) + :Icon(Icons.star_border, size: 20, color: Colors.white), + Text( + '${model.flutter_ui_info.stargazersCount.toString()}', + style: TextStyle(color: Colors.white) + ) + ], + ), + ), + ); + } + ); + } + @override Widget build(BuildContext context) { Store.setWidgetCtx(context); // 初始化scaffold的上下文作为全局上下文,提供弹窗等使用。 return Scaffold( drawer: renderDrawer(), bottomNavigationBar: _bottomNavigationBar(), + floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + floatingActionButton: _floatingActionButton(), body: PageView( controller: _pageController, physics: NeverScrollableScrollPhysics(), diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index caf9102..bd2eb8e 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -6,8 +6,15 @@ import '../objects/user_info.dart' show UserInfo; import '../objects/github_resp_info.dart' show GitHubRespInfo; import 'package:efox_flutter/http/index.dart' as Http; import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; +import 'package:efox_flutter/config/index.dart' show owner_repo; +import '../objects/flutter_ui_info.dart' show FlutterUiInfo; -class UserModel with ChangeNotifier { +class UserModelInfo { + bool isStar = false; // 用户是否star了flutter ui项目 + FlutterUiInfo flutter_ui_info = FlutterUiInfo(); // flutter ui项目信息 +} + +class UserModel extends UserModelInfo with ChangeNotifier { UserInfo user = UserInfo(); Future testLogin() async { return await Http.post(url: 'http://www.baidu.com').then((res) { @@ -91,12 +98,14 @@ class UserModel with ChangeNotifier { getLocalUserInfo() async { String data = await LocalStorage.get('githubUserInfo'); print("本地数据 $data"); + if (data != null) { + UserInfo user = UserInfo.fromJson(json.decode(data)); + setUserInfo(user); + return true; + } if (data == null) { - getUserInfo(); - return; + return await getUserInfo(); } - UserInfo user = UserInfo.fromJson(json.decode(data)); - setUserInfo(user); } /** @@ -120,4 +129,78 @@ class UserModel with ChangeNotifier { LocalStorage.remove('githubRespLoginToken'); notifyListeners(); } + + /** + * 修改star显示 + */ + changeIsStar(isShow){ + isStar = isShow; + notifyListeners(); + } + + /** + * 获取flutter ui star数量 + */ + getFlutterUIStar() { + var response = Http.get( + url: 'https://api.github.com/repos/$owner_repo' + ); + response.then((resp) { + print('获取flutter ui信息:$resp'); + flutter_ui_info= FlutterUiInfo.fromJson(resp.data); + notifyListeners(); + }).catchError((error) { + print('获取flutter ui信息出错:$error'); + }); + } + + /** + * 获取用户的star flutter ui信息 + */ + getUserStar() async { + var response = Http.get( + url: 'https://api.github.com/user/starred/$owner_repo' + ); + response.then((resp) { + print('用户的star信息状态码:${resp.statusCode}'); + if (resp.statusCode == 204) { + // TODO user have focused the repository + isStar = true; + notifyListeners(); + return ; + } + if (resp.statusCode == 404) { + // TODO user have not focused the repository + isStar = false; + notifyListeners(); + return; + } + }).catchError((error) { + print('获取用户star信息出错$error'); + if (error.statusCode == 404) { + // TODO user have not focused the repository + isStar = false; + notifyListeners(); + } + }); + } + + /** + * 用户star flutter ui项目 + */ + setStarFlutterUI() { + var response = Http.put( + url: 'https://api.github.com/user/starred/$owner_repo' + ); + response.then((resp) { + print('用户star flutter ui项目状态码: ${resp.statusCode}'); + if (resp.statusCode == 204) { + // star success + getFlutterUIStar(); + getUserStar(); + } + }).catchError((error) { + print('用户star flutter ui项目错误: $error'); + }); + } } diff --git a/lib/store/objects/flutter_ui_info.dart b/lib/store/objects/flutter_ui_info.dart new file mode 100644 index 0000000..af34786 --- /dev/null +++ b/lib/store/objects/flutter_ui_info.dart @@ -0,0 +1,492 @@ +class FlutterUiInfo { + int id; + String nodeId; + String name; + String fullName; + bool private; + Owner owner; + String htmlUrl; + String description; + bool fork; + String url; + String forksUrl; + String keysUrl; + String collaboratorsUrl; + String teamsUrl; + String hooksUrl; + String issueEventsUrl; + String eventsUrl; + String assigneesUrl; + String branchesUrl; + String tagsUrl; + String blobsUrl; + String gitTagsUrl; + String gitRefsUrl; + String treesUrl; + String statusesUrl; + String languagesUrl; + String stargazersUrl; + String contributorsUrl; + String subscribersUrl; + String subscriptionUrl; + String commitsUrl; + String gitCommitsUrl; + String commentsUrl; + String issueCommentUrl; + String contentsUrl; + String compareUrl; + String mergesUrl; + String archiveUrl; + String downloadsUrl; + String issuesUrl; + String pullsUrl; + String milestonesUrl; + String notificationsUrl; + String labelsUrl; + String releasesUrl; + String deploymentsUrl; + String createdAt; + String updatedAt; + String pushedAt; + String gitUrl; + String sshUrl; + String cloneUrl; + String svnUrl; + String homepage; + int size; + int stargazersCount; // star数量 + int watchersCount; + String language; + bool hasIssues; + bool hasProjects; + bool hasDownloads; + bool hasWiki; + bool hasPages; + int forksCount; // fork数量 + Null mirrorUrl; + bool archived; + bool disabled; + int openIssuesCount; + Null license; + int forks; + int openIssues; + int watchers; + String defaultBranch; + Organization organization; + int networkCount; + int subscribersCount; + + FlutterUiInfo( + {this.id, + this.nodeId, + this.name, + this.fullName, + this.private, + this.owner, + this.htmlUrl, + this.description, + this.fork, + this.url, + this.forksUrl, + this.keysUrl, + this.collaboratorsUrl, + this.teamsUrl, + this.hooksUrl, + this.issueEventsUrl, + this.eventsUrl, + this.assigneesUrl, + this.branchesUrl, + this.tagsUrl, + this.blobsUrl, + this.gitTagsUrl, + this.gitRefsUrl, + this.treesUrl, + this.statusesUrl, + this.languagesUrl, + this.stargazersUrl, + this.contributorsUrl, + this.subscribersUrl, + this.subscriptionUrl, + this.commitsUrl, + this.gitCommitsUrl, + this.commentsUrl, + this.issueCommentUrl, + this.contentsUrl, + this.compareUrl, + this.mergesUrl, + this.archiveUrl, + this.downloadsUrl, + this.issuesUrl, + this.pullsUrl, + this.milestonesUrl, + this.notificationsUrl, + this.labelsUrl, + this.releasesUrl, + this.deploymentsUrl, + this.createdAt, + this.updatedAt, + this.pushedAt, + this.gitUrl, + this.sshUrl, + this.cloneUrl, + this.svnUrl, + this.homepage, + this.size, + this.stargazersCount, + this.watchersCount, + this.language, + this.hasIssues, + this.hasProjects, + this.hasDownloads, + this.hasWiki, + this.hasPages, + this.forksCount, + this.mirrorUrl, + this.archived, + this.disabled, + this.openIssuesCount, + this.license, + this.forks, + this.openIssues, + this.watchers, + this.defaultBranch, + this.organization, + this.networkCount, + this.subscribersCount}); + + FlutterUiInfo.fromJson(Map json) { + id = json['id']; + nodeId = json['node_id']; + name = json['name']; + fullName = json['full_name']; + private = json['private']; + owner = json['owner'] != null ? new Owner.fromJson(json['owner']) : null; + htmlUrl = json['html_url']; + description = json['description']; + fork = json['fork']; + url = json['url']; + forksUrl = json['forks_url']; + keysUrl = json['keys_url']; + collaboratorsUrl = json['collaborators_url']; + teamsUrl = json['teams_url']; + hooksUrl = json['hooks_url']; + issueEventsUrl = json['issue_events_url']; + eventsUrl = json['events_url']; + assigneesUrl = json['assignees_url']; + branchesUrl = json['branches_url']; + tagsUrl = json['tags_url']; + blobsUrl = json['blobs_url']; + gitTagsUrl = json['git_tags_url']; + gitRefsUrl = json['git_refs_url']; + treesUrl = json['trees_url']; + statusesUrl = json['statuses_url']; + languagesUrl = json['languages_url']; + stargazersUrl = json['stargazers_url']; + contributorsUrl = json['contributors_url']; + subscribersUrl = json['subscribers_url']; + subscriptionUrl = json['subscription_url']; + commitsUrl = json['commits_url']; + gitCommitsUrl = json['git_commits_url']; + commentsUrl = json['comments_url']; + issueCommentUrl = json['issue_comment_url']; + contentsUrl = json['contents_url']; + compareUrl = json['compare_url']; + mergesUrl = json['merges_url']; + archiveUrl = json['archive_url']; + downloadsUrl = json['downloads_url']; + issuesUrl = json['issues_url']; + pullsUrl = json['pulls_url']; + milestonesUrl = json['milestones_url']; + notificationsUrl = json['notifications_url']; + labelsUrl = json['labels_url']; + releasesUrl = json['releases_url']; + deploymentsUrl = json['deployments_url']; + createdAt = json['created_at']; + updatedAt = json['updated_at']; + pushedAt = json['pushed_at']; + gitUrl = json['git_url']; + sshUrl = json['ssh_url']; + cloneUrl = json['clone_url']; + svnUrl = json['svn_url']; + homepage = json['homepage']; + size = json['size']; + stargazersCount = json['stargazers_count']; + watchersCount = json['watchers_count']; + language = json['language']; + hasIssues = json['has_issues']; + hasProjects = json['has_projects']; + hasDownloads = json['has_downloads']; + hasWiki = json['has_wiki']; + hasPages = json['has_pages']; + forksCount = json['forks_count']; + mirrorUrl = json['mirror_url']; + archived = json['archived']; + disabled = json['disabled']; + openIssuesCount = json['open_issues_count']; + license = json['license']; + forks = json['forks']; + openIssues = json['open_issues']; + watchers = json['watchers']; + defaultBranch = json['default_branch']; + organization = json['organization'] != null + ? new Organization.fromJson(json['organization']) + : null; + networkCount = json['network_count']; + subscribersCount = json['subscribers_count']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['node_id'] = this.nodeId; + data['name'] = this.name; + data['full_name'] = this.fullName; + data['private'] = this.private; + if (this.owner != null) { + data['owner'] = this.owner.toJson(); + } + data['html_url'] = this.htmlUrl; + data['description'] = this.description; + data['fork'] = this.fork; + data['url'] = this.url; + data['forks_url'] = this.forksUrl; + data['keys_url'] = this.keysUrl; + data['collaborators_url'] = this.collaboratorsUrl; + data['teams_url'] = this.teamsUrl; + data['hooks_url'] = this.hooksUrl; + data['issue_events_url'] = this.issueEventsUrl; + data['events_url'] = this.eventsUrl; + data['assignees_url'] = this.assigneesUrl; + data['branches_url'] = this.branchesUrl; + data['tags_url'] = this.tagsUrl; + data['blobs_url'] = this.blobsUrl; + data['git_tags_url'] = this.gitTagsUrl; + data['git_refs_url'] = this.gitRefsUrl; + data['trees_url'] = this.treesUrl; + data['statuses_url'] = this.statusesUrl; + data['languages_url'] = this.languagesUrl; + data['stargazers_url'] = this.stargazersUrl; + data['contributors_url'] = this.contributorsUrl; + data['subscribers_url'] = this.subscribersUrl; + data['subscription_url'] = this.subscriptionUrl; + data['commits_url'] = this.commitsUrl; + data['git_commits_url'] = this.gitCommitsUrl; + data['comments_url'] = this.commentsUrl; + data['issue_comment_url'] = this.issueCommentUrl; + data['contents_url'] = this.contentsUrl; + data['compare_url'] = this.compareUrl; + data['merges_url'] = this.mergesUrl; + data['archive_url'] = this.archiveUrl; + data['downloads_url'] = this.downloadsUrl; + data['issues_url'] = this.issuesUrl; + data['pulls_url'] = this.pullsUrl; + data['milestones_url'] = this.milestonesUrl; + data['notifications_url'] = this.notificationsUrl; + data['labels_url'] = this.labelsUrl; + data['releases_url'] = this.releasesUrl; + data['deployments_url'] = this.deploymentsUrl; + data['created_at'] = this.createdAt; + data['updated_at'] = this.updatedAt; + data['pushed_at'] = this.pushedAt; + data['git_url'] = this.gitUrl; + data['ssh_url'] = this.sshUrl; + data['clone_url'] = this.cloneUrl; + data['svn_url'] = this.svnUrl; + data['homepage'] = this.homepage; + data['size'] = this.size; + data['stargazers_count'] = this.stargazersCount; + data['watchers_count'] = this.watchersCount; + data['language'] = this.language; + data['has_issues'] = this.hasIssues; + data['has_projects'] = this.hasProjects; + data['has_downloads'] = this.hasDownloads; + data['has_wiki'] = this.hasWiki; + data['has_pages'] = this.hasPages; + data['forks_count'] = this.forksCount; + data['mirror_url'] = this.mirrorUrl; + data['archived'] = this.archived; + data['disabled'] = this.disabled; + data['open_issues_count'] = this.openIssuesCount; + data['license'] = this.license; + data['forks'] = this.forks; + data['open_issues'] = this.openIssues; + data['watchers'] = this.watchers; + data['default_branch'] = this.defaultBranch; + if (this.organization != null) { + data['organization'] = this.organization.toJson(); + } + data['network_count'] = this.networkCount; + data['subscribers_count'] = this.subscribersCount; + return data; + } +} + +class Owner { + String login; + int id; + String nodeId; + String avatarUrl; + String gravatarId; + String url; + String htmlUrl; + String followersUrl; + String followingUrl; + String gistsUrl; + String starredUrl; + String subscriptionsUrl; + String organizationsUrl; + String reposUrl; + String eventsUrl; + String receivedEventsUrl; + String type; + bool siteAdmin; + + Owner( + {this.login, + this.id, + this.nodeId, + this.avatarUrl, + this.gravatarId, + this.url, + this.htmlUrl, + this.followersUrl, + this.followingUrl, + this.gistsUrl, + this.starredUrl, + this.subscriptionsUrl, + this.organizationsUrl, + this.reposUrl, + this.eventsUrl, + this.receivedEventsUrl, + this.type, + this.siteAdmin}); + + Owner.fromJson(Map json) { + login = json['login']; + id = json['id']; + nodeId = json['node_id']; + avatarUrl = json['avatar_url']; + gravatarId = json['gravatar_id']; + url = json['url']; + htmlUrl = json['html_url']; + followersUrl = json['followers_url']; + followingUrl = json['following_url']; + gistsUrl = json['gists_url']; + starredUrl = json['starred_url']; + subscriptionsUrl = json['subscriptions_url']; + organizationsUrl = json['organizations_url']; + reposUrl = json['repos_url']; + eventsUrl = json['events_url']; + receivedEventsUrl = json['received_events_url']; + type = json['type']; + siteAdmin = json['site_admin']; + } + + Map toJson() { + final Map data = new Map(); + data['login'] = this.login; + data['id'] = this.id; + data['node_id'] = this.nodeId; + data['avatar_url'] = this.avatarUrl; + data['gravatar_id'] = this.gravatarId; + data['url'] = this.url; + data['html_url'] = this.htmlUrl; + data['followers_url'] = this.followersUrl; + data['following_url'] = this.followingUrl; + data['gists_url'] = this.gistsUrl; + data['starred_url'] = this.starredUrl; + data['subscriptions_url'] = this.subscriptionsUrl; + data['organizations_url'] = this.organizationsUrl; + data['repos_url'] = this.reposUrl; + data['events_url'] = this.eventsUrl; + data['received_events_url'] = this.receivedEventsUrl; + data['type'] = this.type; + data['site_admin'] = this.siteAdmin; + return data; + } +} + +class Organization { + String login; + int id; + String nodeId; + String avatarUrl; + String gravatarId; + String url; + String htmlUrl; + String followersUrl; + String followingUrl; + String gistsUrl; + String starredUrl; + String subscriptionsUrl; + String organizationsUrl; + String reposUrl; + String eventsUrl; + String receivedEventsUrl; + String type; + bool siteAdmin; + + Organization( + {this.login, + this.id, + this.nodeId, + this.avatarUrl, + this.gravatarId, + this.url, + this.htmlUrl, + this.followersUrl, + this.followingUrl, + this.gistsUrl, + this.starredUrl, + this.subscriptionsUrl, + this.organizationsUrl, + this.reposUrl, + this.eventsUrl, + this.receivedEventsUrl, + this.type, + this.siteAdmin}); + + Organization.fromJson(Map json) { + login = json['login']; + id = json['id']; + nodeId = json['node_id']; + avatarUrl = json['avatar_url']; + gravatarId = json['gravatar_id']; + url = json['url']; + htmlUrl = json['html_url']; + followersUrl = json['followers_url']; + followingUrl = json['following_url']; + gistsUrl = json['gists_url']; + starredUrl = json['starred_url']; + subscriptionsUrl = json['subscriptions_url']; + organizationsUrl = json['organizations_url']; + reposUrl = json['repos_url']; + eventsUrl = json['events_url']; + receivedEventsUrl = json['received_events_url']; + type = json['type']; + siteAdmin = json['site_admin']; + } + + Map toJson() { + final Map data = new Map(); + data['login'] = this.login; + data['id'] = this.id; + data['node_id'] = this.nodeId; + data['avatar_url'] = this.avatarUrl; + data['gravatar_id'] = this.gravatarId; + data['url'] = this.url; + data['html_url'] = this.htmlUrl; + data['followers_url'] = this.followersUrl; + data['following_url'] = this.followingUrl; + data['gists_url'] = this.gistsUrl; + data['starred_url'] = this.starredUrl; + data['subscriptions_url'] = this.subscriptionsUrl; + data['organizations_url'] = this.organizationsUrl; + data['repos_url'] = this.reposUrl; + data['events_url'] = this.eventsUrl; + data['received_events_url'] = this.receivedEventsUrl; + data['type'] = this.type; + data['site_admin'] = this.siteAdmin; + return data; + } +} \ No newline at end of file From 1ffe0b378719c72043e1cbaf8d76f3d750989d5f Mon Sep 17 00:00:00 2001 From: linhaoran <1476589247@qq.com> Date: Wed, 5 Jun 2019 17:08:19 +0800 Subject: [PATCH 077/100] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=20=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E6=96=B9=E5=BA=93tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/comment/index.dart | 25 +++++++++++++++++++++++++ lib/page/home.dart | 19 +++++++++++++++++-- lib/page/library/index.dart | 25 +++++++++++++++++++++++++ locale/en.json | 2 ++ locale/zh.json | 2 ++ 5 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 lib/page/comment/index.dart create mode 100644 lib/page/library/index.dart diff --git a/lib/page/comment/index.dart b/lib/page/comment/index.dart new file mode 100644 index 0000000..645e45d --- /dev/null +++ b/lib/page/comment/index.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:efox_flutter/lang/index.dart' show AppLocalizations; + +class Index extends StatefulWidget { + @override + _IndexState createState() => _IndexState(); +} + +class _IndexState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + centerTitle: true, + title: Text( + AppLocalizations.$t('title_comment') + ), + ), + body: Center( + child: Text('评论'), + ), + ); + } +} \ No newline at end of file diff --git a/lib/page/home.dart b/lib/page/home.dart index 8c52dff..39b73c4 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -5,6 +5,8 @@ import 'package:efox_flutter/config/theme.dart' show AppTheme; import 'component/tabs.dart' as TabIndex; import 'mine/index.dart' as MyIndex; import 'app_login/index.dart' as LoginIndex; +import 'comment/index.dart' as CommentIndex; +import 'library/index.dart' as LibraryIndex; import 'package:efox_flutter/store/index.dart' show Store, UserModel; @@ -34,10 +36,17 @@ class _IndexState extends State { shape: CircularNotchedRectangle(), clipBehavior: Clip.antiAlias, child: BottomNavigationBar( + type: BottomNavigationBarType.fixed, items: [ BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_component')), icon: Icon(Icons.dashboard)), + BottomNavigationBarItem( + title: Text(AppLocalizations.$t('title_comment')), + icon: Icon(Icons.comment)), + BottomNavigationBarItem( + title: Text(AppLocalizations.$t('title_library')), + icon: Icon(Icons.library_add)), BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_my')), icon: Icon(Icons.person_outline)), @@ -127,7 +136,7 @@ class _IndexState extends State { ); } - Widget _floatingActionButton() { + Widget _floatingActionButton(context) { return Store.connect( builder: (context, child, model) { return FloatingActionButton( @@ -146,6 +155,10 @@ class _IndexState extends State { } ) ); + } else { + Scaffold.of(context).showSnackBar(SnackBar( + content: Text('已star'), + )); } } }, @@ -175,7 +188,7 @@ class _IndexState extends State { drawer: renderDrawer(), bottomNavigationBar: _bottomNavigationBar(), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - floatingActionButton: _floatingActionButton(), + floatingActionButton: _floatingActionButton(context), body: PageView( controller: _pageController, physics: NeverScrollableScrollPhysics(), @@ -186,6 +199,8 @@ class _IndexState extends State { }, children: [ TabIndex.Index(), + CommentIndex.Index(), + LibraryIndex.Index(), MyIndex.Index(), ], ), diff --git a/lib/page/library/index.dart b/lib/page/library/index.dart new file mode 100644 index 0000000..2e710ea --- /dev/null +++ b/lib/page/library/index.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:efox_flutter/lang/index.dart' show AppLocalizations; + +class Index extends StatefulWidget { + @override + _IndexState createState() => _IndexState(); +} + +class _IndexState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + centerTitle: true, + title: Text( + AppLocalizations.$t('title_library') + ), + ), + body: Center( + child: Text('第三方库'), + ), + ); + } +} \ No newline at end of file diff --git a/locale/en.json b/locale/en.json index 8702fc7..577c3a1 100644 --- a/locale/en.json +++ b/locale/en.json @@ -1,6 +1,8 @@ { "title_component": "Components", "title_my": "My", + "title_comment": "comment", + "title_library": "library", "common": { "login": "Sign In", "logout": "Logout" diff --git a/locale/zh.json b/locale/zh.json index f1dd90f..e44054f 100644 --- a/locale/zh.json +++ b/locale/zh.json @@ -1,6 +1,8 @@ { "title_component": "组件", "title_my": "我的", + "title_comment": "评论", + "title_library": "第三方库", "common": { "login": "登录", "logout": "退出" From 7e04ed4ee661d28f78d952692301e80e21a98006 Mon Sep 17 00:00:00 2001 From: linhaoran <1476589247@qq.com> Date: Tue, 11 Jun 2019 18:22:49 +0800 Subject: [PATCH 078/100] =?UTF-8?q?feat=EF=BC=9A=20=E6=B7=BB=E5=8A=A0issue?= =?UTF-8?q?=E8=AF=84=E8=AE=BA=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/comment/index.dart | 101 ++++++++- lib/page/home.dart | 3 + lib/store/models/user_model.dart | 22 ++ lib/store/objects/flutter_ui_issues.dart | 254 +++++++++++++++++++++++ 4 files changed, 377 insertions(+), 3 deletions(-) create mode 100644 lib/store/objects/flutter_ui_issues.dart diff --git a/lib/page/comment/index.dart b/lib/page/comment/index.dart index 645e45d..000f54a 100644 --- a/lib/page/comment/index.dart +++ b/lib/page/comment/index.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; +import 'package:efox_flutter/store/index.dart' show Store, UserModel; +import 'package:efox_flutter/store/objects/flutter_ui_issues.dart' show IssuesContent; class Index extends StatefulWidget { @override @@ -17,9 +19,102 @@ class _IndexState extends State { AppLocalizations.$t('title_comment') ), ), - body: Center( - child: Text('评论'), - ), + body: Container( + padding: EdgeInsets.fromLTRB(16.0, 0 ,16.0, 0), + child: _CommentList(context), + ) + ); + } + + Widget _CommentList(BuildContext context){ + return Store.connect( + builder: (context, child, model) { + if (model.flutter_ui_issues!=null&&model.flutter_ui_issues.issuesContent!=null&&model.flutter_ui_issues.issuesContent.length!=0) { + return ListView.builder( + itemCount: model.flutter_ui_issues.issuesContent.length, + itemBuilder: (context, index) { + return _CommentCard(context, model.flutter_ui_issues.issuesContent[index]); + }, + ); + } else { + return Center( + child: Text('loading....'), + ); + } + } + ); + } + + Widget _CommentCard(BuildContext context, IssuesContent issuesContent) { + return Card( + elevation: 4.0, + child: Stack( + alignment: Alignment.topCenter, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: 16/9, + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(6.0), + topRight: Radius.circular(6.0), + bottomLeft: Radius.circular(6.0), + bottomRight: Radius.circular(6.0) + ), + child: Image.network( + issuesContent.user.avatarUrl??'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', + fit: BoxFit.cover, + ), + ), + ), + Container( + padding: EdgeInsets.fromLTRB(20.0, 20, 20, 0), + child: Text( + '${issuesContent.title != '' ? issuesContent.title : '无标题'} #${issuesContent.number}', + style: Theme.of(context).textTheme.title, + ), + ), + Container( + padding: EdgeInsets.fromLTRB(20.0, 10, 20, 0), + child: Column( + children: [ + Text( + '创建时间:${issuesContent.createdAt}', + style: TextStyle(color: Colors.black54, fontSize: 12) + ), + Text( + '更新时间:${issuesContent.updatedAt}', + style: TextStyle(color: Colors.black54, fontSize: 12) + ) + ], + ) + ), + Container( + padding: EdgeInsets.fromLTRB(20.0, 10, 20, 20.0), + child: Text( + issuesContent.body, + // != '' ?issuesContent.body:'无主体内容' + style: Theme.of(context).textTheme.subhead + ), + ) + ], + ), + Positioned( + top: 10, + left: 10, + child: Text( + issuesContent.user.login, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + fontSize: 20 + ), + ), + ) + ], + ) ); } } \ No newline at end of file diff --git a/lib/page/home.dart b/lib/page/home.dart index 39b73c4..1dca3a1 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -54,6 +54,9 @@ class _IndexState extends State { // type: BottomNavigationBarType.fixed, currentIndex: _currentIndex, onTap: (int index) { + if(index == 1&&_currentIndex!=index) { + Store.value(context).getIssueFlutterUI(); + } _pageController.jumpToPage(index); }, ), diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index bd2eb8e..582d5fe 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -8,10 +8,12 @@ import 'package:efox_flutter/http/index.dart' as Http; import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; import 'package:efox_flutter/config/index.dart' show owner_repo; import '../objects/flutter_ui_info.dart' show FlutterUiInfo; +import '../objects/flutter_ui_issues.dart' show FlutterUiIssues; class UserModelInfo { bool isStar = false; // 用户是否star了flutter ui项目 FlutterUiInfo flutter_ui_info = FlutterUiInfo(); // flutter ui项目信息 + FlutterUiIssues flutter_ui_issues = FlutterUiIssues(); // flutter ui的issues内容 } class UserModel extends UserModelInfo with ChangeNotifier { @@ -203,4 +205,24 @@ class UserModel extends UserModelInfo with ChangeNotifier { print('用户star flutter ui项目错误: $error'); }); } + + /** + * 获取flutter ui的issue内容 + */ + getIssueFlutterUI() { + var response = Http.get( + url: 'https://api.github.com/repos/efoxTeam/flutter-ui/issues' + ); + response.then((resp) { + var data = { + "issues_content": resp.data + }; + print('获取flutter ui的issue内容:${data}'); + flutter_ui_issues = FlutterUiIssues.fromJson(data); + notifyListeners(); + return flutter_ui_issues; + }).catchError((error) { + print('获取flutter ui的issue内容出错:$error'); + }); + } } diff --git a/lib/store/objects/flutter_ui_issues.dart b/lib/store/objects/flutter_ui_issues.dart new file mode 100644 index 0000000..ddf1f82 --- /dev/null +++ b/lib/store/objects/flutter_ui_issues.dart @@ -0,0 +1,254 @@ +class FlutterUiIssues { + List issuesContent; + + FlutterUiIssues({this.issuesContent}); + + FlutterUiIssues.fromJson(Map json) { + if (json['issues_content'] != null) { + issuesContent = new List(); + json['issues_content'].forEach((v) { + issuesContent.add(new IssuesContent.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + if (this.issuesContent != null) { + data['issues_content'] = + this.issuesContent.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class IssuesContent { + String url; + String repositoryUrl; + String labelsUrl; + String commentsUrl; + String eventsUrl; + String htmlUrl; + int id; + String nodeId; + int number; + String title; + User user; + List labels; + String state; + bool locked; + List assignees; + int comments; + String createdAt; + String updatedAt; + String authorAssociation; + String body; + + IssuesContent( + {this.url, + this.repositoryUrl, + this.labelsUrl, + this.commentsUrl, + this.eventsUrl, + this.htmlUrl, + this.id, + this.nodeId, + this.number, + this.title, + this.user, + this.labels, + this.state, + this.locked, + this.assignees, + this.comments, + this.createdAt, + this.updatedAt, + this.authorAssociation, + this.body}); + + IssuesContent.fromJson(Map json) { + url = json['url']; + repositoryUrl = json['repository_url']; + labelsUrl = json['labels_url']; + commentsUrl = json['comments_url']; + eventsUrl = json['events_url']; + htmlUrl = json['html_url']; + id = json['id']; + nodeId = json['node_id']; + number = json['number']; + title = json['title']; + user = json['user'] != null ? new User.fromJson(json['user']) : null; + labels = json['labels'].cast(); + state = json['state']; + locked = json['locked']; + assignees = json['assignees'].cast(); + comments = json['comments']; + createdAt = json['created_at']; + updatedAt = json['updated_at']; + authorAssociation = json['author_association']; + body = json['body']; + } + + Map toJson() { + final Map data = new Map(); + data['url'] = this.url; + data['repository_url'] = this.repositoryUrl; + data['labels_url'] = this.labelsUrl; + data['comments_url'] = this.commentsUrl; + data['events_url'] = this.eventsUrl; + data['html_url'] = this.htmlUrl; + data['id'] = this.id; + data['node_id'] = this.nodeId; + data['number'] = this.number; + data['title'] = this.title; + if (this.user != null) { + data['user'] = this.user.toJson(); + } + data['labels'] = this.labels; + data['state'] = this.state; + data['locked'] = this.locked; + data['assignees'] = this.assignees; + data['comments'] = this.comments; + data['created_at'] = this.createdAt; + data['updated_at'] = this.updatedAt; + data['author_association'] = this.authorAssociation; + data['body'] = this.body; + return data; + } +} + +class User { + String login; + int id; + String nodeId; + String avatarUrl; + String gravatarId; + String url; + String htmlUrl; + String followersUrl; + String followingUrl; + String gistsUrl; + String starredUrl; + String subscriptionsUrl; + String organizationsUrl; + String reposUrl; + String eventsUrl; + String receivedEventsUrl; + String type; + bool siteAdmin; + + User( + {this.login, + this.id, + this.nodeId, + this.avatarUrl, + this.gravatarId, + this.url, + this.htmlUrl, + this.followersUrl, + this.followingUrl, + this.gistsUrl, + this.starredUrl, + this.subscriptionsUrl, + this.organizationsUrl, + this.reposUrl, + this.eventsUrl, + this.receivedEventsUrl, + this.type, + this.siteAdmin}); + + User.fromJson(Map json) { + login = json['login']; + id = json['id']; + nodeId = json['node_id']; + avatarUrl = json['avatar_url']; + gravatarId = json['gravatar_id']; + url = json['url']; + htmlUrl = json['html_url']; + followersUrl = json['followers_url']; + followingUrl = json['following_url']; + gistsUrl = json['gists_url']; + starredUrl = json['starred_url']; + subscriptionsUrl = json['subscriptions_url']; + organizationsUrl = json['organizations_url']; + reposUrl = json['repos_url']; + eventsUrl = json['events_url']; + receivedEventsUrl = json['received_events_url']; + type = json['type']; + siteAdmin = json['site_admin']; + } + + Map toJson() { + final Map data = new Map(); + data['login'] = this.login; + data['id'] = this.id; + data['node_id'] = this.nodeId; + data['avatar_url'] = this.avatarUrl; + data['gravatar_id'] = this.gravatarId; + data['url'] = this.url; + data['html_url'] = this.htmlUrl; + data['followers_url'] = this.followersUrl; + data['following_url'] = this.followingUrl; + data['gists_url'] = this.gistsUrl; + data['starred_url'] = this.starredUrl; + data['subscriptions_url'] = this.subscriptionsUrl; + data['organizations_url'] = this.organizationsUrl; + data['repos_url'] = this.reposUrl; + data['events_url'] = this.eventsUrl; + data['received_events_url'] = this.receivedEventsUrl; + data['type'] = this.type; + data['site_admin'] = this.siteAdmin; + return data; + } +} + + + + + +// [ +// { +// "url": "https://api.github.com/repos/efoxTeam/flutter-ui/issues/53", +// "repository_url": "https://api.github.com/repos/efoxTeam/flutter-ui", +// "labels_url": "https://api.github.com/repos/efoxTeam/flutter-ui/issues/53/labels{/name}", +// "comments_url": "https://api.github.com/repos/efoxTeam/flutter-ui/issues/53/comments", +// "events_url": "https://api.github.com/repos/efoxTeam/flutter-ui/issues/53/events", +// "html_url": "https://github.com/efoxTeam/flutter-ui/issues/53", +// "id": 454062553, +// "node_id": "MDU6SXNzdWU0NTQwNjI1NTM=", +// "number": 53, +// "title": "test issue", +// "user": { +// "login": "DIVINER-onlys", +// "id": 35843543, +// "node_id": "MDQ6VXNlcjM1ODQzNTQz", +// "avatar_url": "https://avatars0.githubusercontent.com/u/35843543?v=4", +// "gravatar_id": "", +// "url": "https://api.github.com/users/DIVINER-onlys", +// "html_url": "https://github.com/DIVINER-onlys", +// "followers_url": "https://api.github.com/users/DIVINER-onlys/followers", +// "following_url": "https://api.github.com/users/DIVINER-onlys/following{/other_user}", +// "gists_url": "https://api.github.com/users/DIVINER-onlys/gists{/gist_id}", +// "starred_url": "https://api.github.com/users/DIVINER-onlys/starred{/owner}{/repo}", +// "subscriptions_url": "https://api.github.com/users/DIVINER-onlys/subscriptions", +// "organizations_url": "https://api.github.com/users/DIVINER-onlys/orgs", +// "repos_url": "https://api.github.com/users/DIVINER-onlys/repos", +// "events_url": "https://api.github.com/users/DIVINER-onlys/events{/privacy}", +// "received_events_url": "https://api.github.com/users/DIVINER-onlys/received_events", +// "type": "User", +// "site_admin": false +// }, +// "labels": [""], +// "state": "open", +// "locked": false, +// "assignee": null, +// "assignees": [""], +// "milestone": null, +// "comments": 1, +// "created_at": "2019-06-10T08:47:37Z", +// "updated_at": "2019-06-10T08:51:54Z", +// "closed_at": null, +// "author_association": "NONE", +// "body": "api test" +// } +// ] \ No newline at end of file From d49bce4b351bd27d24c9fc2aeabe48363221f3d5 Mon Sep 17 00:00:00 2001 From: linhaoran <1476589247@qq.com> Date: Wed, 12 Jun 2019 15:39:30 +0800 Subject: [PATCH 079/100] =?UTF-8?q?feat:=20issue=E8=AF=84=E8=AE=BA?= =?UTF-8?q?=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/comment/details.dart | 139 ++++++++++++++++++ lib/page/comment/index.dart | 135 ++++++++--------- lib/router/index.dart | 14 ++ lib/store/models/user_model.dart | 23 ++- lib/store/objects/issues_comment.dart | 199 ++++++++++++++++++++++++++ locale/en.json | 1 + locale/zh.json | 1 + 7 files changed, 447 insertions(+), 65 deletions(-) create mode 100644 lib/page/comment/details.dart create mode 100644 lib/store/objects/issues_comment.dart diff --git a/lib/page/comment/details.dart b/lib/page/comment/details.dart new file mode 100644 index 0000000..43d4dc3 --- /dev/null +++ b/lib/page/comment/details.dart @@ -0,0 +1,139 @@ +import 'package:flutter/material.dart'; +import 'package:efox_flutter/lang/index.dart' show AppLocalizations; +import 'package:efox_flutter/store/index.dart' show Store, UserModel; +import 'package:efox_flutter/store/objects/flutter_ui_issues.dart' show IssuesContent; +import 'package:efox_flutter/store/objects/issues_comment.dart' show IssuesDetails; + +class Index extends StatefulWidget { + int indexes; + Index({ Key key, @required this.indexes }):super(key: key); + @override + _IndexState createState() => _IndexState(); +} + +class _IndexState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + centerTitle: true, + title: Text( + AppLocalizations.$t('title_comment_detials') + ), + ), + body: Container( + child: _ContentList(context) + ), + ); + } + + Widget _ContentList (BuildContext context) { + return ListView( + children: [ + _IssueContent(context), + _CommentContent(context) + ], + ); + } + + Widget _IssueContent (BuildContext context) { + return Store.connect( + builder: (context, child, model) { + IssuesContent issuesContent = model.flutter_ui_issues.issuesContent[widget.indexes]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + leading: CircleAvatar( + backgroundImage: NetworkImage( + issuesContent.user.avatarUrl??'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', + ), + ), + title: Text('${issuesContent.user.login}'), + subtitle: Text('更新时间:${issuesContent.updatedAt}'), + ), + Container( + padding: EdgeInsets.fromLTRB(20.0, 20, 20, 0), + child: Text( + '${issuesContent.title != '' ? issuesContent.title : '无标题'} #${issuesContent.number}', + style: Theme.of(context).textTheme.title, + ), + ), + Container( + padding: EdgeInsets.fromLTRB(20.0, 10, 20, 20.0), + child: Text( + issuesContent.body, + // != '' ?issuesContent.body:'无主体内容' + style: Theme.of(context).textTheme.subhead + ), + ), + Divider( + height: 1, + ) + ], + ); + } + ); + } + + Widget _CommentContent (BuildContext context) { + return FutureBuilder( + future: _getIssueComment(context), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Container( + padding: EdgeInsets.all(20), + child: Center( + child: Text('loading....'), + ), + ); + } else if(snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasData) { + return Store.connect( + builder: (context, child, model) { + List items = []; + for(var issuesDetails in model.issues_comment.issuesDetails) { + items.add(_CommentContentItem(context, issuesDetails)); + } + return Column( + children: items, + ); + } + ); + } else { + return Center( + child: Text('暂无数据'), + ); + } + } + }, + ); + } + + Future _getIssueComment(BuildContext context) async { + IssuesContent issuesContent = Store.value(context).flutter_ui_issues.issuesContent[widget.indexes]; + await Store.value(context).getIssueComment(issuesContent.number); + return 'end'; + } + + Widget _CommentContentItem(BuildContext context, IssuesDetails issuesDetails) { + return Container( + padding: EdgeInsets.fromLTRB(10, 20, 10, 0), + child: Column( + children: [ + ListTile( + leading: CircleAvatar( + backgroundImage: NetworkImage( + issuesDetails.user.avatarUrl??'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', + ), + ), + title: Text('${issuesDetails.user.login}'), + subtitle: Text('${issuesDetails.body}') + ), + Divider(height: 1,) + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/page/comment/index.dart b/lib/page/comment/index.dart index 000f54a..dc83abd 100644 --- a/lib/page/comment/index.dart +++ b/lib/page/comment/index.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/store/index.dart' show Store, UserModel; import 'package:efox_flutter/store/objects/flutter_ui_issues.dart' show IssuesContent; +import 'package:efox_flutter/router/index.dart' show FluroRouter; class Index extends StatefulWidget { @override @@ -33,7 +34,7 @@ class _IndexState extends State { return ListView.builder( itemCount: model.flutter_ui_issues.issuesContent.length, itemBuilder: (context, index) { - return _CommentCard(context, model.flutter_ui_issues.issuesContent[index]); + return _CommentCard(context, model.flutter_ui_issues.issuesContent[index], index); }, ); } else { @@ -45,76 +46,82 @@ class _IndexState extends State { ); } - Widget _CommentCard(BuildContext context, IssuesContent issuesContent) { - return Card( - elevation: 4.0, - child: Stack( - alignment: Alignment.topCenter, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AspectRatio( - aspectRatio: 16/9, - child: ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(6.0), - topRight: Radius.circular(6.0), - bottomLeft: Radius.circular(6.0), - bottomRight: Radius.circular(6.0) + Widget _CommentCard(BuildContext context, IssuesContent issuesContent, int index) { + return GestureDetector( + onTap: () { + // Store.value(context).getIssueComment(issuesContent.number); + FluroRouter.router.navigateTo(context, '/commentdetails?indexes=${index}',); + }, + child: Card( + elevation: 4.0, + child: Stack( + alignment: Alignment.topCenter, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: 16/9, + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(6.0), + topRight: Radius.circular(6.0), + bottomLeft: Radius.circular(6.0), + bottomRight: Radius.circular(6.0) + ), + child: Image.network( + issuesContent.user.avatarUrl??'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', + fit: BoxFit.cover, + ), ), - child: Image.network( - issuesContent.user.avatarUrl??'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', - fit: BoxFit.cover, + ), + Container( + padding: EdgeInsets.fromLTRB(20.0, 20, 20, 0), + child: Text( + '${issuesContent.title != '' ? issuesContent.title : '无标题'} #${issuesContent.number}', + style: Theme.of(context).textTheme.title, ), ), - ), - Container( - padding: EdgeInsets.fromLTRB(20.0, 20, 20, 0), - child: Text( - '${issuesContent.title != '' ? issuesContent.title : '无标题'} #${issuesContent.number}', - style: Theme.of(context).textTheme.title, + Container( + padding: EdgeInsets.fromLTRB(20.0, 10, 20, 0), + child: Column( + children: [ + Text( + '创建时间:${issuesContent.createdAt}', + style: TextStyle(color: Colors.black54, fontSize: 12) + ), + Text( + '更新时间:${issuesContent.updatedAt}', + style: TextStyle(color: Colors.black54, fontSize: 12) + ) + ], + ) ), - ), - Container( - padding: EdgeInsets.fromLTRB(20.0, 10, 20, 0), - child: Column( - children: [ - Text( - '创建时间:${issuesContent.createdAt}', - style: TextStyle(color: Colors.black54, fontSize: 12) - ), - Text( - '更新时间:${issuesContent.updatedAt}', - style: TextStyle(color: Colors.black54, fontSize: 12) - ) - ], + Container( + padding: EdgeInsets.fromLTRB(20.0, 10, 20, 20.0), + child: Text( + issuesContent.body, + // != '' ?issuesContent.body:'无主体内容' + style: Theme.of(context).textTheme.subhead + ), ) - ), - Container( - padding: EdgeInsets.fromLTRB(20.0, 10, 20, 20.0), - child: Text( - issuesContent.body, - // != '' ?issuesContent.body:'无主体内容' - style: Theme.of(context).textTheme.subhead + ], + ), + Positioned( + top: 10, + left: 10, + child: Text( + issuesContent.user.login, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + fontSize: 20 ), - ) - ], - ), - Positioned( - top: 10, - left: 10, - child: Text( - issuesContent.user.login, - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - fontSize: 20 ), - ), - ) - ], - ) + ) + ], + ) + ), ); } } \ No newline at end of file diff --git a/lib/router/index.dart b/lib/router/index.dart index 1841321..6ff106a 100644 --- a/lib/router/index.dart +++ b/lib/router/index.dart @@ -2,6 +2,8 @@ import 'package:flutter/widgets.dart'; import 'package:fluro/fluro.dart'; //首页 import 'package:efox_flutter/page/home.dart' as HomePage; +// 评论详细页面 +import 'package:efox_flutter/page/comment/details.dart' as CommentDetails; import 'package:efox_flutter/widget/index.dart' as WidgetConfig; import 'handles.dart'; //统计 @@ -24,6 +26,18 @@ class FluroRouter { }, ), ); + // 评论详情页面 + router.define( + '/commentdetails', + handler: Handler( + handlerFunc: (BuildContext context, Map params) { + String indexes = params["indexes"]?.first; + return CommentDetails.Index( + indexes: int.parse('${indexes}') + ); + } + ) + ); router.define('/webview', handler: webviewHandler); diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index 582d5fe..c65fe95 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -9,11 +9,13 @@ import 'package:efox_flutter/utils/localStorage.dart' show LocalStorage; import 'package:efox_flutter/config/index.dart' show owner_repo; import '../objects/flutter_ui_info.dart' show FlutterUiInfo; import '../objects/flutter_ui_issues.dart' show FlutterUiIssues; +import '../objects/issues_comment.dart' show IssuesComment; class UserModelInfo { bool isStar = false; // 用户是否star了flutter ui项目 FlutterUiInfo flutter_ui_info = FlutterUiInfo(); // flutter ui项目信息 FlutterUiIssues flutter_ui_issues = FlutterUiIssues(); // flutter ui的issues内容 + IssuesComment issues_comment = IssuesComment(); // issues comment内容 } class UserModel extends UserModelInfo with ChangeNotifier { @@ -211,7 +213,7 @@ class UserModel extends UserModelInfo with ChangeNotifier { */ getIssueFlutterUI() { var response = Http.get( - url: 'https://api.github.com/repos/efoxTeam/flutter-ui/issues' + url: 'https://api.github.com/repos/$owner_repo/issues' ); response.then((resp) { var data = { @@ -225,4 +227,23 @@ class UserModel extends UserModelInfo with ChangeNotifier { print('获取flutter ui的issue内容出错:$error'); }); } + + /** + * 获取对应issue下的回复内容 + */ + getIssueComment(int number) async { + var response = await Http.get( + url: 'https://api.github.com/repos/$owner_repo/issues/$number/comments' + ); + try { + var data = { + "issues_details": response.data + }; + print('获取对应issue下的回复内容:${response.data}'); + issues_comment = IssuesComment.fromJson(data); + notifyListeners(); + } catch(error) { + print('获取对应issue下的回复内容出错:$error'); + } + } } diff --git a/lib/store/objects/issues_comment.dart b/lib/store/objects/issues_comment.dart new file mode 100644 index 0000000..aefc74a --- /dev/null +++ b/lib/store/objects/issues_comment.dart @@ -0,0 +1,199 @@ +class IssuesComment { + List issuesDetails; + + IssuesComment({this.issuesDetails}); + + IssuesComment.fromJson(Map json) { + if (json['issues_details'] != null) { + issuesDetails = new List(); + json['issues_details'].forEach((v) { + issuesDetails.add(new IssuesDetails.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + if (this.issuesDetails != null) { + data['issues_details'] = + this.issuesDetails.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class IssuesDetails { + String url; + String htmlUrl; + String issueUrl; + int id; + String nodeId; + User user; + String createdAt; + String updatedAt; + String authorAssociation; + String body; + + IssuesDetails( + {this.url, + this.htmlUrl, + this.issueUrl, + this.id, + this.nodeId, + this.user, + this.createdAt, + this.updatedAt, + this.authorAssociation, + this.body}); + + IssuesDetails.fromJson(Map json) { + url = json['url']; + htmlUrl = json['html_url']; + issueUrl = json['issue_url']; + id = json['id']; + nodeId = json['node_id']; + user = json['user'] != null ? new User.fromJson(json['user']) : null; + createdAt = json['created_at']; + updatedAt = json['updated_at']; + authorAssociation = json['author_association']; + body = json['body']; + } + + Map toJson() { + final Map data = new Map(); + data['url'] = this.url; + data['html_url'] = this.htmlUrl; + data['issue_url'] = this.issueUrl; + data['id'] = this.id; + data['node_id'] = this.nodeId; + if (this.user != null) { + data['user'] = this.user.toJson(); + } + data['created_at'] = this.createdAt; + data['updated_at'] = this.updatedAt; + data['author_association'] = this.authorAssociation; + data['body'] = this.body; + return data; + } +} + +class User { + String login; + int id; + String nodeId; + String avatarUrl; + String gravatarId; + String url; + String htmlUrl; + String followersUrl; + String followingUrl; + String gistsUrl; + String starredUrl; + String subscriptionsUrl; + String organizationsUrl; + String reposUrl; + String eventsUrl; + String receivedEventsUrl; + String type; + bool siteAdmin; + + User( + {this.login, + this.id, + this.nodeId, + this.avatarUrl, + this.gravatarId, + this.url, + this.htmlUrl, + this.followersUrl, + this.followingUrl, + this.gistsUrl, + this.starredUrl, + this.subscriptionsUrl, + this.organizationsUrl, + this.reposUrl, + this.eventsUrl, + this.receivedEventsUrl, + this.type, + this.siteAdmin}); + + User.fromJson(Map json) { + login = json['login']; + id = json['id']; + nodeId = json['node_id']; + avatarUrl = json['avatar_url']; + gravatarId = json['gravatar_id']; + url = json['url']; + htmlUrl = json['html_url']; + followersUrl = json['followers_url']; + followingUrl = json['following_url']; + gistsUrl = json['gists_url']; + starredUrl = json['starred_url']; + subscriptionsUrl = json['subscriptions_url']; + organizationsUrl = json['organizations_url']; + reposUrl = json['repos_url']; + eventsUrl = json['events_url']; + receivedEventsUrl = json['received_events_url']; + type = json['type']; + siteAdmin = json['site_admin']; + } + + Map toJson() { + final Map data = new Map(); + data['login'] = this.login; + data['id'] = this.id; + data['node_id'] = this.nodeId; + data['avatar_url'] = this.avatarUrl; + data['gravatar_id'] = this.gravatarId; + data['url'] = this.url; + data['html_url'] = this.htmlUrl; + data['followers_url'] = this.followersUrl; + data['following_url'] = this.followingUrl; + data['gists_url'] = this.gistsUrl; + data['starred_url'] = this.starredUrl; + data['subscriptions_url'] = this.subscriptionsUrl; + data['organizations_url'] = this.organizationsUrl; + data['repos_url'] = this.reposUrl; + data['events_url'] = this.eventsUrl; + data['received_events_url'] = this.receivedEventsUrl; + data['type'] = this.type; + data['site_admin'] = this.siteAdmin; + return data; + } +} + +// { +// "issues_details": [ +// { +// "url": "https://api.github.com/repos/efoxTeam/flutter-ui/issues/comments/500342145", +// "html_url": "https://github.com/efoxTeam/flutter-ui/issues/53#issuecomment-500342145", +// "issue_url": "https://api.github.com/repos/efoxTeam/flutter-ui/issues/53", +// "id": 500342145, +// "node_id": "MDEyOklzc3VlQ29tbWVudDUwMDM0MjE0NQ==", +// "user": { +// "login": "DIVINER-onlys", +// "id": 35843543, +// "node_id": "MDQ6VXNlcjM1ODQzNTQz", +// "avatar_url": "https://avatars0.githubusercontent.com/u/35843543?v=4", +// "gravatar_id": "", +// "url": "https://api.github.com/users/DIVINER-onlys", +// "html_url": "https://github.com/DIVINER-onlys", +// "followers_url": "https://api.github.com/users/DIVINER-onlys/followers", +// "following_url": "https://api.github.com/users/DIVINER-onlys/following{/other_user}", +// "gists_url": "https://api.github.com/users/DIVINER-onlys/gists{/gist_id}", +// "starred_url": "https://api.github.com/users/DIVINER-onlys/starred{/owner}{/repo}", +// "subscriptions_url": "https://api.github.com/users/DIVINER-onlys/subscriptions", +// "organizations_url": "https://api.github.com/users/DIVINER-onlys/orgs", +// "repos_url": "https://api.github.com/users/DIVINER-onlys/repos", +// "events_url": "https://api.github.com/users/DIVINER-onlys/events{/privacy}", +// "received_events_url": "https://api.github.com/users/DIVINER-onlys/received_events", +// "type": "User", +// "site_admin": false +// }, +// "created_at": "2019-06-10T08:51:54Z", +// "updated_at": "2019-06-10T08:51:54Z", +// "author_association": "NONE", +// "body": "ooooo" +// } +// ] +// } \ No newline at end of file diff --git a/locale/en.json b/locale/en.json index 577c3a1..f5ab3e0 100644 --- a/locale/en.json +++ b/locale/en.json @@ -2,6 +2,7 @@ "title_component": "Components", "title_my": "My", "title_comment": "comment", + "title_comment_detials": "Comment details", "title_library": "library", "common": { "login": "Sign In", diff --git a/locale/zh.json b/locale/zh.json index e44054f..f091824 100644 --- a/locale/zh.json +++ b/locale/zh.json @@ -2,6 +2,7 @@ "title_component": "组件", "title_my": "我的", "title_comment": "评论", + "title_comment_detials": "评论详情", "title_library": "第三方库", "common": { "login": "登录", From 0f7ab5a7081768f0369d3ec2cdc188b2c49d8dd1 Mon Sep 17 00:00:00 2001 From: linhaoran <1476589247@qq.com> Date: Mon, 17 Jun 2019 17:42:13 +0800 Subject: [PATCH 080/100] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0issue?= =?UTF-8?q?=E5=9B=9E=E5=A4=8D=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/comment/details.dart | 146 +++++++++++++++++++++++++++++-- lib/store/models/user_model.dart | 24 +++++ 2 files changed, 164 insertions(+), 6 deletions(-) diff --git a/lib/page/comment/details.dart b/lib/page/comment/details.dart index 43d4dc3..424a68b 100644 --- a/lib/page/comment/details.dart +++ b/lib/page/comment/details.dart @@ -3,6 +3,7 @@ import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/store/index.dart' show Store, UserModel; import 'package:efox_flutter/store/objects/flutter_ui_issues.dart' show IssuesContent; import 'package:efox_flutter/store/objects/issues_comment.dart' show IssuesDetails; +import 'package:efox_flutter/page/app_login/index.dart' as LoginIndex; class Index extends StatefulWidget { int indexes; @@ -12,6 +13,17 @@ class Index extends StatefulWidget { } class _IndexState extends State { + final TextEditingController _controller = TextEditingController(); + var _getComment; + bool isCanSend = false; + + @override + void initState() { + // TODO: implement initState + super.initState(); + _getComment = this._getIssueComment(context); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -22,9 +34,15 @@ class _IndexState extends State { AppLocalizations.$t('title_comment_detials') ), ), - body: Container( - child: _ContentList(context) - ), + body: Stack( + children: [ + Container( + margin: EdgeInsets.only(bottom: 50), + child: _ContentList(context), + ), + _SendComment(context) + ], + ) ); } @@ -79,7 +97,7 @@ class _IndexState extends State { Widget _CommentContent (BuildContext context) { return FutureBuilder( - future: _getIssueComment(context), + future: _getComment, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Container( @@ -112,8 +130,8 @@ class _IndexState extends State { } Future _getIssueComment(BuildContext context) async { - IssuesContent issuesContent = Store.value(context).flutter_ui_issues.issuesContent[widget.indexes]; - await Store.value(context).getIssueComment(issuesContent.number); + IssuesContent issuesContent = Store.valueNotCtx().flutter_ui_issues.issuesContent[widget.indexes]; + await Store.valueNotCtx().getIssueComment(issuesContent.number); return 'end'; } @@ -136,4 +154,120 @@ class _IndexState extends State { ), ); } + + Widget _SendComment (BuildContext context) { + return Positioned( + bottom: 0, + left: 0, + child: Container( + width: MediaQuery.of(context).size.width, + height: 50, + decoration: BoxDecoration( + color: Colors.white, + border: Border( + top: BorderSide(width: 0.5, color: Color(int.parse('0xffe4e4e4'))) + ) + ), + child: Row( + children: [ + Expanded( + flex: 1, + child: _InputBox(), + ), + Store.connect( + builder: (context, child, model) { + IssuesContent issuesContent = model.flutter_ui_issues.issuesContent[widget.indexes]; + return GestureDetector( + onTap: () async { + if (isCanSend) { + if (model.user.id != null) { + print('发布内容:${_controller.text}'); + bool isSendSuccess = await model.setIssueComment( + _controller.text, + issuesContent.number + ); + if (isSendSuccess) { + await this._getIssueComment(context); + _controller.text = ''; + } else { + print('网络错误'); + Scaffold.of(context).showSnackBar(SnackBar( + content: Text('网络出错,请稍后重试'), + )); + } + } else { + print('去往登陆'); + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return LoginIndex.Index(); + } + ) + ); + } + } + }, + child: Container( + padding: EdgeInsets.fromLTRB(0, 0, 10, 0), + child: Text( + '发布', + style: TextStyle( + color: isCanSend ? Theme.of(context).primaryColor : Colors.grey, + fontSize: 17 + ) + ), + ), + ); + } + ) + ], + ) + ), + ); + } + Widget _InputBox() { + return Container( + height: 30, + margin: EdgeInsets.fromLTRB(10, 10, 10, 10), + decoration: BoxDecoration( + color: Color(int.parse('0xffEDEDED')), + borderRadius: BorderRadius.circular(15) + ), + child: Row( + children: [ + Expanded( + flex: 1, + child: TextField( + controller: _controller, + autofocus: false, + onChanged: _onChanged, + style: TextStyle( + fontSize: 18.0, + color: Colors.black, + fontWeight: FontWeight.w300 + ), + decoration: InputDecoration( + contentPadding: EdgeInsets.fromLTRB(10, 0, 10, 0), + border: InputBorder.none, + hintText: '说点什么吧', + hintStyle: TextStyle(fontSize: 15) + ), + ), + ) + ], + ) + ); + } + + _onChanged(String text) { + if (text.length > 0) { + setState(() { + isCanSend = true; + }); + } else { + setState(() { + isCanSend = false; + }); + } + } } \ No newline at end of file diff --git a/lib/store/models/user_model.dart b/lib/store/models/user_model.dart index c65fe95..74a02eb 100644 --- a/lib/store/models/user_model.dart +++ b/lib/store/models/user_model.dart @@ -246,4 +246,28 @@ class UserModel extends UserModelInfo with ChangeNotifier { print('获取对应issue下的回复内容出错:$error'); } } + + /** + * 回复issue评论 + */ + setIssueComment(String text, int number) async { + var data = { + "body": "$text" + }; + var response = await Http.post( + url: 'https://api.github.com/repos/$owner_repo/issues/$number/comments', + data: data + ); + try { + print('回复issue评论状态码:${response.statusCode}'); + if (response.statusCode == 201) { + return true; + } else { + return false; + } + } catch (error) { + print('回复issue评论出错:$error'); + return false; + } + } } From 2e6bd93a10a404fc92022c53ced227afff960004 Mon Sep 17 00:00:00 2001 From: linhaoran <1476589247@qq.com> Date: Mon, 17 Jun 2019 17:50:20 +0800 Subject: [PATCH 081/100] =?UTF-8?q?fixed:=20=E4=BF=AE=E6=94=B9issue?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=B1=95=E7=A4=BA=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/page/comment/index.dart | 58 ++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/lib/page/comment/index.dart b/lib/page/comment/index.dart index dc83abd..8639788 100644 --- a/lib/page/comment/index.dart +++ b/lib/page/comment/index.dart @@ -60,20 +60,32 @@ class _IndexState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AspectRatio( - aspectRatio: 16/9, - child: ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(6.0), - topRight: Radius.circular(6.0), - bottomLeft: Radius.circular(6.0), - bottomRight: Radius.circular(6.0) - ), - child: Image.network( + // AspectRatio( + // aspectRatio: 16/9, + // child: ClipRRect( + // borderRadius: BorderRadius.only( + // topLeft: Radius.circular(6.0), + // topRight: Radius.circular(6.0), + // bottomLeft: Radius.circular(6.0), + // bottomRight: Radius.circular(6.0) + // ), + // child: Image.network( + // issuesContent.user.avatarUrl??'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', + // fit: BoxFit.cover, + // ), + // ), + // ), + ListTile( + leading: CircleAvatar( + backgroundImage: NetworkImage( issuesContent.user.avatarUrl??'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', - fit: BoxFit.cover, ), ), + title: Text('${issuesContent.user.login}', style: TextStyle(color: Theme.of(context).primaryColor),), + subtitle: Text('更新时间:${issuesContent.updatedAt}'), + trailing: Icon( + Icons.keyboard_arrow_right + ), ), Container( padding: EdgeInsets.fromLTRB(20.0, 20, 20, 0), @@ -107,18 +119,18 @@ class _IndexState extends State { ) ], ), - Positioned( - top: 10, - left: 10, - child: Text( - issuesContent.user.login, - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - fontSize: 20 - ), - ), - ) + // Positioned( + // top: 10, + // left: 10, + // child: Text( + // issuesContent.user.login, + // style: TextStyle( + // color: Theme.of(context).primaryColor, + // fontWeight: FontWeight.bold, + // fontSize: 20 + // ), + // ), + // ) ], ) ), From 0b7adebc91ee2329bda149b417523801400ec30c Mon Sep 17 00:00:00 2001 From: KenZR Date: Thu, 20 Jun 2019 17:39:15 +0800 Subject: [PATCH 082/100] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8Diphonex=E5=BA=95?= =?UTF-8?q?=E9=83=A8=E9=97=AE=E9=A2=98=EF=BC=8C=E4=BF=AE=E5=A4=8D=E7=99=BB?= =?UTF-8?q?=E9=99=86=E5=94=94=E8=BF=94=E5=9B=9E=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 19 +-- lib/page/app_login/index.dart | 231 ++++++++++++++------------- lib/page/comment/index.dart | 224 +++++++++++++------------- lib/page/home.dart | 88 +++++----- 4 files changed, 280 insertions(+), 282 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 64cb2f4..cc9ce98 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -197,6 +197,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -278,21 +279,17 @@ buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/flutter_downloader/FlutterDownloaderDatabase.bundle", ); name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - ); outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FlutterDownloaderDatabase.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; showEnvVarsInLog = 0; }; EB6D2D2E068567EE260A8427 /* [CP] Embed Pods Frameworks */ = { @@ -301,7 +298,7 @@ files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; @@ -310,7 +307,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -414,7 +411,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.flutter.beer; + PRODUCT_BUNDLE_IDENTIFIER = com.flutter.ui; PRODUCT_NAME = "$(TARGET_NAME)"; VERSIONING_SYSTEM = "apple-generic"; }; @@ -540,7 +537,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.flutter.beer; + PRODUCT_BUNDLE_IDENTIFIER = com.flutter.ui; PRODUCT_NAME = "$(TARGET_NAME)"; VERSIONING_SYSTEM = "apple-generic"; }; @@ -564,7 +561,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.flutter.beer; + PRODUCT_BUNDLE_IDENTIFIER = com.flutter.ui; PRODUCT_NAME = "$(TARGET_NAME)"; VERSIONING_SYSTEM = "apple-generic"; }; diff --git a/lib/page/app_login/index.dart b/lib/page/app_login/index.dart index 87f0391..f1d77b5 100644 --- a/lib/page/app_login/index.dart +++ b/lib/page/app_login/index.dart @@ -66,134 +66,135 @@ class _IndexState extends State { @override Widget build(BuildContext ctx) { - return WillPopScope( - child: Scaffold( - appBar: AppBar( - centerTitle: true, - title: Text( - AppLocalizations.$t('common.login'), - textAlign: TextAlign.center, - ), - automaticallyImplyLeading: false, + // return WillPopScope( + // child: , + // onWillPop: () { + // beforeDispose(); + // }, + // ); + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: Text( + AppLocalizations.$t('common.login'), + textAlign: TextAlign.center, ), - body: Builder(builder: (BuildContext context) { - return SingleChildScrollView( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 50, horizontal: 24), - child: Form( - key: _formKey, - autovalidate: true, - child: Column( - children: [ - renderGithubImage(), - TextFormField( - controller: nameCtl, - autofocus: true, - decoration: InputDecoration( - labelText: AppLocalizations.$t('login.account'), - hintText: AppLocalizations.$t('login.account_tips'), - icon: Icon(Icons.person), - ), - validator: (v) { - return v.trim().length > 0 - ? null - : AppLocalizations.$t('login.account_error_tips'); - }, - ), - TextFormField( - controller: pwdCtl, - decoration: InputDecoration( - labelText: AppLocalizations.$t('login.password'), - hintText: AppLocalizations.$t('login.password_tips'), - icon: Icon(Icons.lock), - ), - obscureText: true, - validator: (v) { - return v.trim().length > 0 - ? null - : AppLocalizations.$t('login.password_error_tips'); - }, - ), - Padding( - padding: EdgeInsets.only(top: 50), - child: Row( - children: [ - Expanded( - child: RaisedButton( - padding: EdgeInsets.all(15), - color: Theme.of(context).primaryColor, - textColor: Theme.of(context) - .primaryTextTheme - .title - .color, - child: Text( - AppLocalizations.$t('common.login'), - ), - onPressed: () async { - if ((_formKey.currentState as FormState) - .validate()) { - await Store.value(context) - .loginController(context, { - 'name': nameCtl.text.trim(), - 'pwd': pwdCtl.text.trim() - }); - await Store.value(context).getUserStar(); - } - }, - ), - ) - ], - ), + // automaticallyImplyLeading: false, + ), + body: Builder(builder: (BuildContext context) { + return SingleChildScrollView( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 50, horizontal: 24), + child: Form( + key: _formKey, + autovalidate: true, + child: Column( + children: [ + renderGithubImage(), + TextFormField( + controller: nameCtl, + autofocus: false, + decoration: InputDecoration( + labelText: AppLocalizations.$t('login.account'), + hintText: AppLocalizations.$t('login.account_tips'), + icon: Icon(Icons.person), ), - SizedBox( - height: 10, + validator: (v) { + return v.trim().length > 0 + ? null + : AppLocalizations.$t('login.account_error_tips'); + }, + ), + TextFormField( + controller: pwdCtl, + decoration: InputDecoration( + labelText: AppLocalizations.$t('login.password'), + hintText: AppLocalizations.$t('login.password_tips'), + icon: Icon(Icons.lock), ), - Row( - mainAxisAlignment: MainAxisAlignment.center, + obscureText: true, + validator: (v) { + return v.trim().length > 0 + ? null + : AppLocalizations.$t('login.password_error_tips'); + }, + ), + Padding( + padding: EdgeInsets.only(top: 50), + child: Row( children: [ - GestureDetector( - child: Text( - 'Github账户登录说明', - style: TextStyle( - decoration: TextDecoration.underline, - textBaseline: TextBaseline.ideographic, - decorationColor: Color(0xff000000), + Expanded( + child: RaisedButton( + padding: EdgeInsets.all(15), + color: Theme.of(context).primaryColor, + textColor: + Theme.of(context).primaryTextTheme.title.color, + child: Text( + AppLocalizations.$t('common.login'), ), + onPressed: () async { + if ((_formKey.currentState as FormState) + .validate()) { + await Store.value(context) + .loginController(context, { + 'name': nameCtl.text.trim(), + 'pwd': pwdCtl.text.trim() + }); + await Store.value(context) + .getUserStar(); + } + }, + ), + ) + ], + ), + ), + SizedBox( + height: 10, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + child: Text( + 'Github账户登录说明', + style: TextStyle( + decoration: TextDecoration.underline, + textBaseline: TextBaseline.ideographic, + decorationColor: Color(0xff000000), ), - onTap: () { - renderOverlay(" Text 1"); - }, - ), - SizedBox( - width: 10, ), - GestureDetector( - child: Text( - '软件许可及服务协议', - style: TextStyle( - decoration: TextDecoration.underline, - textBaseline: TextBaseline.ideographic, - decorationColor: const Color(0xff000000), - ), + onTap: () { + renderOverlay(" Text 1"); + }, + ), + SizedBox( + width: 10, + ), + GestureDetector( + child: Text( + '软件许可及服务协议', + style: TextStyle( + decoration: TextDecoration.underline, + textBaseline: TextBaseline.ideographic, + decorationColor: const Color(0xff000000), ), - onTap: () { - renderOverlay(" Text 2"); - }, ), - ], - ) - ], - ), + onTap: () { + renderOverlay(" Text 2"); + }, + ), + ], + ) + ], ), ), - ); - }), - ), - onWillPop: () { - beforeDispose(); - }, + ), + ); + }), ); } + beforeDispose() { if (_overlayEntry != null) { _overlayEntry.remove(); diff --git a/lib/page/comment/index.dart b/lib/page/comment/index.dart index 8639788..3a07402 100644 --- a/lib/page/comment/index.dart +++ b/lib/page/comment/index.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; import 'package:efox_flutter/store/index.dart' show Store, UserModel; -import 'package:efox_flutter/store/objects/flutter_ui_issues.dart' show IssuesContent; +import 'package:efox_flutter/store/objects/flutter_ui_issues.dart' + show IssuesContent; import 'package:efox_flutter/router/index.dart' show FluroRouter; class Index extends StatefulWidget { @@ -13,127 +14,128 @@ class _IndexState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - elevation: 0, - centerTitle: true, - title: Text( - AppLocalizations.$t('title_comment') + appBar: AppBar( + elevation: 0, + centerTitle: true, + title: Text(AppLocalizations.$t('title_comment')), ), - ), - body: Container( - padding: EdgeInsets.fromLTRB(16.0, 0 ,16.0, 0), - child: _CommentList(context), - ) - ); + body: Container( + padding: EdgeInsets.fromLTRB(16.0, 0, 16.0, 0), + child: _CommentList(context), + )); } - Widget _CommentList(BuildContext context){ - return Store.connect( - builder: (context, child, model) { - if (model.flutter_ui_issues!=null&&model.flutter_ui_issues.issuesContent!=null&&model.flutter_ui_issues.issuesContent.length!=0) { - return ListView.builder( - itemCount: model.flutter_ui_issues.issuesContent.length, - itemBuilder: (context, index) { - return _CommentCard(context, model.flutter_ui_issues.issuesContent[index], index); - }, - ); - } else { - return Center( - child: Text('loading....'), - ); - } + Widget _CommentList(BuildContext context) { + return Store.connect(builder: (context, child, model) { + if (model.flutter_ui_issues != null && + model.flutter_ui_issues.issuesContent != null && + model.flutter_ui_issues.issuesContent.length != 0) { + return ListView.builder( + itemCount: model.flutter_ui_issues.issuesContent.length, + itemBuilder: (context, index) { + return _CommentCard( + context, model.flutter_ui_issues.issuesContent[index], index); + }, + ); + } else { + return Center( + child: Text('loading....'), + ); } - ); + }); } - Widget _CommentCard(BuildContext context, IssuesContent issuesContent, int index) { - return GestureDetector( - onTap: () { - // Store.value(context).getIssueComment(issuesContent.number); - FluroRouter.router.navigateTo(context, '/commentdetails?indexes=${index}',); - }, - child: Card( - elevation: 4.0, - child: Stack( - alignment: Alignment.topCenter, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, + Widget _CommentCard( + BuildContext context, IssuesContent issuesContent, int index) { + return Padding( + child: GestureDetector( + onTap: () { + // Store.value(context).getIssueComment(issuesContent.number); + FluroRouter.router.navigateTo( + context, + '/commentdetails?indexes=${index}', + ); + }, + child: Card( + elevation: 4.0, + child: Stack( + alignment: Alignment.topCenter, children: [ - // AspectRatio( - // aspectRatio: 16/9, - // child: ClipRRect( - // borderRadius: BorderRadius.only( - // topLeft: Radius.circular(6.0), - // topRight: Radius.circular(6.0), - // bottomLeft: Radius.circular(6.0), - // bottomRight: Radius.circular(6.0) - // ), - // child: Image.network( - // issuesContent.user.avatarUrl??'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', - // fit: BoxFit.cover, - // ), - // ), - // ), - ListTile( - leading: CircleAvatar( - backgroundImage: NetworkImage( - issuesContent.user.avatarUrl??'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // AspectRatio( + // aspectRatio: 16/9, + // child: ClipRRect( + // borderRadius: BorderRadius.only( + // topLeft: Radius.circular(6.0), + // topRight: Radius.circular(6.0), + // bottomLeft: Radius.circular(6.0), + // bottomRight: Radius.circular(6.0) + // ), + // child: Image.network( + // issuesContent.user.avatarUrl??'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', + // fit: BoxFit.cover, + // ), + // ), + // ), + ListTile( + leading: CircleAvatar( + backgroundImage: NetworkImage( + issuesContent.user.avatarUrl ?? + 'http://thumb10.jfcdns.com/2018-06/bce5b10ae530f530.png', + ), + ), + title: Text( + '${issuesContent.user.login}', + style: TextStyle(color: Theme.of(context).primaryColor), + ), + subtitle: Text('更新时间:${issuesContent.updatedAt}'), + trailing: Icon(Icons.keyboard_arrow_right), ), - ), - title: Text('${issuesContent.user.login}', style: TextStyle(color: Theme.of(context).primaryColor),), - subtitle: Text('更新时间:${issuesContent.updatedAt}'), - trailing: Icon( - Icons.keyboard_arrow_right - ), - ), - Container( - padding: EdgeInsets.fromLTRB(20.0, 20, 20, 0), - child: Text( - '${issuesContent.title != '' ? issuesContent.title : '无标题'} #${issuesContent.number}', - style: Theme.of(context).textTheme.title, - ), - ), - Container( - padding: EdgeInsets.fromLTRB(20.0, 10, 20, 0), - child: Column( - children: [ - Text( - '创建时间:${issuesContent.createdAt}', - style: TextStyle(color: Colors.black54, fontSize: 12) + Container( + padding: EdgeInsets.fromLTRB(20.0, 20, 20, 0), + child: Text( + '${issuesContent.title != '' ? issuesContent.title : '无标题'} #${issuesContent.number}', + style: Theme.of(context).textTheme.title, ), - Text( - '更新时间:${issuesContent.updatedAt}', - style: TextStyle(color: Colors.black54, fontSize: 12) - ) - ], - ) + ), + Container( + padding: EdgeInsets.fromLTRB(20.0, 10, 20, 0), + child: Column( + children: [ + Text('创建时间:${issuesContent.createdAt}', + style: TextStyle( + color: Colors.black54, fontSize: 12)), + Text('更新时间:${issuesContent.updatedAt}', + style: TextStyle( + color: Colors.black54, fontSize: 12)) + ], + )), + Container( + padding: EdgeInsets.fromLTRB(20.0, 10, 20, 20.0), + child: Text(issuesContent.body, + // != '' ?issuesContent.body:'无主体内容' + style: Theme.of(context).textTheme.subhead), + ) + ], ), - Container( - padding: EdgeInsets.fromLTRB(20.0, 10, 20, 20.0), - child: Text( - issuesContent.body, - // != '' ?issuesContent.body:'无主体内容' - style: Theme.of(context).textTheme.subhead - ), - ) + // Positioned( + // top: 10, + // left: 10, + // child: Text( + // issuesContent.user.login, + // style: TextStyle( + // color: Theme.of(context).primaryColor, + // fontWeight: FontWeight.bold, + // fontSize: 20 + // ), + // ), + // ) ], - ), - // Positioned( - // top: 10, - // left: 10, - // child: Text( - // issuesContent.user.login, - // style: TextStyle( - // color: Theme.of(context).primaryColor, - // fontWeight: FontWeight.bold, - // fontSize: 20 - // ), - // ), - // ) - ], - ) + )), ), + padding: EdgeInsets.fromLTRB(0, 10, 0, 10), ); } -} \ No newline at end of file +} diff --git a/lib/page/home.dart b/lib/page/home.dart index 1dca3a1..91a88ee 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -33,20 +33,25 @@ class _IndexState extends State { Widget _bottomNavigationBar() { return BottomAppBar( + elevation: 0, + color: Color(0xFFf7f7f7), shape: CircularNotchedRectangle(), clipBehavior: Clip.antiAlias, child: BottomNavigationBar( + //backgroundColor: Color(0xff000000), + elevation: 0, + backgroundColor: Color(0xFFf7f7f7), type: BottomNavigationBarType.fixed, items: [ BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_component')), icon: Icon(Icons.dashboard)), BottomNavigationBarItem( - title: Text(AppLocalizations.$t('title_comment')), - icon: Icon(Icons.comment)), + title: Text(AppLocalizations.$t('title_comment')), + icon: Icon(Icons.comment)), BottomNavigationBarItem( - title: Text(AppLocalizations.$t('title_library')), - icon: Icon(Icons.library_add)), + title: Text(AppLocalizations.$t('title_library')), + icon: Icon(Icons.library_add)), BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_my')), icon: Icon(Icons.person_outline)), @@ -54,7 +59,7 @@ class _IndexState extends State { // type: BottomNavigationBarType.fixed, currentIndex: _currentIndex, onTap: (int index) { - if(index == 1&&_currentIndex!=index) { + if (index == 1 && _currentIndex != index) { Store.value(context).getIssueFlutterUI(); } _pageController.jumpToPage(index); @@ -140,49 +145,42 @@ class _IndexState extends State { } Widget _floatingActionButton(context) { - return Store.connect( - builder: (context, child, model) { - return FloatingActionButton( - backgroundColor: Theme.of(context).primaryColor, - onPressed: () { - if(!model.isStar&&model.user.id != null) { - print('进行star'); - model.setStarFlutterUI(); + return Store.connect(builder: (context, child, model) { + return FloatingActionButton( + backgroundColor: Theme.of(context).primaryColor, + onPressed: () { + if (!model.isStar && model.user.id != null) { + print('进行star'); + model.setStarFlutterUI(); + } else { + print('不满足进行star条件'); + if (model.user.id == null) { + Navigator.of(context) + .push(MaterialPageRoute(builder: (BuildContext context) { + return LoginIndex.Index(); + })); } else { - print('不满足进行star条件'); - if(model.user.id == null) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return LoginIndex.Index(); - } - ) - ); - } else { - Scaffold.of(context).showSnackBar(SnackBar( - content: Text('已star'), - )); - } + Scaffold.of(context).showSnackBar(SnackBar( + content: Text('已star'), + )); } - }, - child: Container( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - model.isStar - ?Icon(Icons.star,size: 20, color: Colors.white) - :Icon(Icons.star_border, size: 20, color: Colors.white), - Text( - '${model.flutter_ui_info.stargazersCount.toString()}', - style: TextStyle(color: Colors.white) - ) - ], - ), + } + }, + child: Container( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + model.isStar + ? Icon(Icons.star, size: 20, color: Colors.white) + : Icon(Icons.star_border, size: 20, color: Colors.white), + Text('${model.flutter_ui_info.stargazersCount.toString()}', + style: TextStyle(color: Colors.white)) + ], ), - ); - } - ); - } + ), + ); + }); + } @override Widget build(BuildContext context) { From 42d1024e540166261b7445770ed4f14c4152c92e Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Thu, 8 Aug 2019 09:56:58 +0800 Subject: [PATCH 083/100] =?UTF-8?q?'feat:=E4=BF=AE=E6=94=B9=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=86=85=E5=AE=B9'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/controller/index.dart | 2 +- lib/main.dart | 2 +- lib/page/home.dart | 8 ++++---- lib/page/mine/index.dart | 4 ++-- lib/store/models/config_state_model.dart | 10 +++++----- lib/widget/scrollview/nestedscrollview/index.dart | 2 +- lib/widget/scrollview/scrollbar/index.dart | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/controller/index.dart b/lib/controller/index.dart index 3f68c27..e3ad404 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -5,7 +5,7 @@ import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; void initState() async { // 获取版本号 - Store.valueNotCtx().$getAppVersion(); + Store.valueNotCtx().getAppVersion(); // 登录 Store.valueNotCtx().getLocalUserInfo().then((res) { if (res) { diff --git a/lib/main.dart b/lib/main.dart index 3e18e27..895c764 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,7 +31,7 @@ class MainAppState extends State { @override Widget build(BuildContext context) { - Store.value(context).$getTheme(); + Store.value(context).getTheme(); return Store.connect( builder: (context, child, model) { diff --git a/lib/page/home.dart b/lib/page/home.dart index ea9df7a..6999709 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -43,15 +43,15 @@ class _IndexState extends State { backgroundColor: Color(0xFFf7f7f7), type: BottomNavigationBarType.fixed, items: [ + BottomNavigationBarItem( + title: Text(AppLocalizations.$t('title_library')), + icon: Icon(Icons.library_add)), BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_component')), icon: Icon(Icons.dashboard)), BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_comment')), icon: Icon(Icons.comment)), - BottomNavigationBarItem( - title: Text(AppLocalizations.$t('title_library')), - icon: Icon(Icons.library_add)), BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_my')), icon: Icon(Icons.person_outline)), @@ -215,9 +215,9 @@ class _IndexState extends State { }); }, children: [ + LibraryIndex.Index(), TabIndex.Index(), CommentIndex.Index(), - LibraryIndex.Index(), MyIndex.Index(), ], ), diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index dadefe8..e60d107 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -113,7 +113,7 @@ class _IndexState extends State { ), ListTile( onTap: () { - Store.value(context).$setIsPro(); + Store.value(context).setIsPro(); }, leading: Icon(Icons.verified_user), title: Text(Store.value(context).isPro @@ -152,7 +152,7 @@ class _IndexState extends State { Widget Edage(name, color, context) { return GestureDetector( onTap: () { - Store.value(context).$setTheme(name); + Store.value(context).setTheme(name); }, child: Container( color: Color(color), diff --git a/lib/store/models/config_state_model.dart b/lib/store/models/config_state_model.dart index 3edcdbf..20064b4 100644 --- a/lib/store/models/config_state_model.dart +++ b/lib/store/models/config_state_model.dart @@ -12,27 +12,27 @@ class ConfigInfo { } class ConfigModel extends ConfigInfo with ChangeNotifier { - Future $getAppVersion() async { + Future getAppVersion() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); appVersion = await packageInfo.version; notifyListeners(); } - Future $getTheme() async { + Future getTheme() async { String _theme = await LocalStorage.get('theme'); print('config get Theme ${_theme}'); if (_theme != null) { - $setTheme(_theme); + setTheme(_theme); } } - Future $setTheme(payload) async { + Future setTheme(payload) async { theme = payload; LocalStorage.set('theme', payload); notifyListeners(); } - Future $setIsPro() async { + Future setIsPro() async { isPro = !isPro; notifyListeners(); } diff --git a/lib/widget/scrollview/nestedscrollview/index.dart b/lib/widget/scrollview/nestedscrollview/index.dart index c512c2e..84593a8 100644 --- a/lib/widget/scrollview/nestedscrollview/index.dart +++ b/lib/widget/scrollview/nestedscrollview/index.dart @@ -5,7 +5,7 @@ import 'demo.dart' as Demo; class Index extends StatefulWidget { static String title = 'NestedScrollView'; static String mdUrl = 'docs/widget/scrollview/nestedscrollview/index.md'; - static String originCodeUrl = 'https://docs.flutter.io/flutter/widgets/Scrollbar-class.html'; + static String originCodeUrl = 'https://docs.flutter.io/flutter/material/Scrollbar-class.html'; @override _IndexState createState() => new _IndexState(); diff --git a/lib/widget/scrollview/scrollbar/index.dart b/lib/widget/scrollview/scrollbar/index.dart index cbc9ce7..9af7dc7 100644 --- a/lib/widget/scrollview/scrollbar/index.dart +++ b/lib/widget/scrollview/scrollbar/index.dart @@ -5,7 +5,7 @@ import 'demo.dart' as Demo; class Index extends StatefulWidget { static String title = 'Scrollbar'; static String mdUrl = 'docs/widget/scrollview/scrollbar/index.md'; - static String originCodeUrl = 'https://docs.flutter.io/flutter/widgets/Scrollbar-class.html'; + static String originCodeUrl = 'https://docs.flutter.io/flutter/material/Scrollbar-class.html'; @override _IndexState createState() => new _IndexState(); From acd81517bfbddbccfa4a9aa239238ebbf3763961 Mon Sep 17 00:00:00 2001 From: wanwu <609780590@qq.com> Date: Thu, 8 Aug 2019 14:10:43 +0800 Subject: [PATCH 084/100] update readme.md update readme.md --- readme.md | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/readme.md b/readme.md index b0f4b52..db1476f 100644 --- a/readme.md +++ b/readme.md @@ -1,27 +1,48 @@ -# Flutter UI v1.0.2 +# Flutter UI v1.0.2 > flutter 开发者社区 ## 功能清单 -+ widget 组件教程 -+ 多语言切换 -+ 多主题切换 -+ 自动更新检测 -+ firebase 崩溃监控 ++ 实战组件集锦【规则中】 ++ widget基础组件介绍【持续更新】 ++ 推文【持续更新】 ++ 多语言切换【√】 ++ 多主题切换【√】 ++ 自动更新检测 【√】 ++ firebase 崩溃监控【√】 + +## 实战组件集锦 ++ 弹层 + + 透明弹窗背景 + + 确认框 + + 聊天输入框 ++ 列表 + + 上下拉加载 ++ 动画 + + SVAG接入 + + 波纹动画 ++ 导航 + + 顶部透明 ++ 适配方案 + + 适配iphonex底部 ## 推文 ++ [Flutter UI APP 低调上线](https://juejin.im/post/5c90514e6fb9a070c859029c) ++ [Flutter 接入 firebase 快速构建应用](https://juejin.im/post/5c90514e6fb9a070c859029c) ++ [Flutter UI 1.0.2落地与优化小结](https://juejin.im/post/5c95e691f265da610c06905c) + [Flutter 全局弹窗](https://juejin.im/post/5c9f2c37518825609415d11d) + [Flutter UI使用Provide实现主题切换](https://juejin.im/post/5ca5e240f265da30c1725021) -+ [Flutter UI 1.0.2落地与优化小结](https://juejin.im/post/5c95e691f265da610c06905c) + [Flutter provide ProvideMulti](https://juejin.im/post/5cc685835188252e8925f056) ++ [以$t形式使用flutter多语言](https://juejin.im/post/5cdb8adee51d453acc60162c) ++ [Flutter Provider 3.0实战教程](https://juejin.im/post/5d2c19c6e51d4558936aa11c) ## apk 下载 -![安卓包下载](readme/apk.png) +![安卓包下载](https://github.com/efoxTeam/flutter-ui/blob/master/readme/apk.png?raw=true) [安卓包下载](https://github.com/efoxTeam/flutter-ui/releases/download/v1.0.2/app-release.apk) ## Demo 预览 - - + + ## 项目相关 + [apk包历史版本](https://github.com/efoxTeam/flutter-ui/releases) @@ -30,7 +51,7 @@ ## 项目交流 - + From 26da1dbca3e91243d8ad543985c603937e4dbbc4 Mon Sep 17 00:00:00 2001 From: wanwusangzhi <609780590@qq.com> Date: Thu, 8 Aug 2019 14:46:58 +0800 Subject: [PATCH 085/100] =?UTF-8?q?'feat:=E4=BF=AE=E6=94=B9=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E5=86=85=E5=AE=B9'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/controller/index.dart | 2 +- lib/main.dart | 2 +- lib/page/mine/index.dart | 2 +- lib/store/models/config_state_model.dart | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/controller/index.dart b/lib/controller/index.dart index c3b0693..76921bd 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -5,7 +5,7 @@ import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; void initState() { // 获取版本号 - Store.valueNotCtx().$getAppVersion(); + Store.valueNotCtx().getAppVersion(); // 登录 Store.valueNotCtx().getLocalUserInfo(); Future.delayed(Duration(seconds: 3), () { diff --git a/lib/main.dart b/lib/main.dart index 3e18e27..895c764 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,7 +31,7 @@ class MainAppState extends State { @override Widget build(BuildContext context) { - Store.value(context).$getTheme(); + Store.value(context).getTheme(); return Store.connect( builder: (context, child, model) { diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index cc14bf4..4cadbd6 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -140,7 +140,7 @@ class _IndexState extends State { Widget Edage(name, color, context) { return GestureDetector( onTap: () { - Store.value(context).$setTheme(name); + Store.value(context).setTheme(name); }, child: Container( color: Color(color), diff --git a/lib/store/models/config_state_model.dart b/lib/store/models/config_state_model.dart index 6f9913e..633118f 100644 --- a/lib/store/models/config_state_model.dart +++ b/lib/store/models/config_state_model.dart @@ -12,21 +12,21 @@ class ConfigInfo { } class ConfigModel extends ConfigInfo with ChangeNotifier { - Future $getAppVersion() async { + Future getAppVersion() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); appVersion = await packageInfo.version; notifyListeners(); } - Future $getTheme() async { + Future getTheme() async { String _theme = await LocalStorage.get('theme'); print('config get Theme ${_theme}'); if (_theme != null) { - $setTheme(_theme); + setTheme(_theme); } } - Future $setTheme(payload) async { + Future setTheme(payload) async { theme = payload; LocalStorage.set('theme', payload); notifyListeners(); From 7807d12c7836d3b4e1dc2f9e49a358112c679b78 Mon Sep 17 00:00:00 2001 From: DIVINER-onlys <35843543+DIVINER-onlys@users.noreply.github.com> Date: Wed, 14 Aug 2019 11:36:24 +0800 Subject: [PATCH 086/100] Set theme jekyll-theme-cayman From ec96ea16958f7b95c2d63a3c63e2a140112b2fc4 Mon Sep 17 00:00:00 2001 From: wanwu <609780590@qq.com> Date: Fri, 16 Aug 2019 18:00:44 +0800 Subject: [PATCH 087/100] update reamde.md update reamde.md --- readme.md | 113 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 38 deletions(-) diff --git a/readme.md b/readme.md index db1476f..c8461d5 100644 --- a/readme.md +++ b/readme.md @@ -1,49 +1,86 @@ # Flutter UI v1.0.2 -> flutter 开发者社区 -## 功能清单 -+ 实战组件集锦【规则中】 -+ widget基础组件介绍【持续更新】 -+ 推文【持续更新】 -+ 多语言切换【√】 -+ 多主题切换【√】 -+ 自动更新检测 【√】 -+ firebase 崩溃监控【√】 - -## 实战组件集锦 -+ 弹层 - + 透明弹窗背景 - + 确认框 - + 聊天输入框 -+ 列表 - + 上下拉加载 -+ 动画 - + SVAG接入 - + 波纹动画 -+ 导航 - + 顶部透明 -+ 适配方案 - + 适配iphonex底部 - -## 推文 -+ [Flutter UI APP 低调上线](https://juejin.im/post/5c90514e6fb9a070c859029c) -+ [Flutter 接入 firebase 快速构建应用](https://juejin.im/post/5c90514e6fb9a070c859029c) -+ [Flutter UI 1.0.2落地与优化小结](https://juejin.im/post/5c95e691f265da610c06905c) -+ [Flutter 全局弹窗](https://juejin.im/post/5c9f2c37518825609415d11d) -+ [Flutter UI使用Provide实现主题切换](https://juejin.im/post/5ca5e240f265da30c1725021) -+ [Flutter provide ProvideMulti](https://juejin.im/post/5cc685835188252e8925f056) -+ [以$t形式使用flutter多语言](https://juejin.im/post/5cdb8adee51d453acc60162c) -+ [Flutter Provider 3.0实战教程](https://juejin.im/post/5d2c19c6e51d4558936aa11c) +> YY Flutter 开发者社区 +> +[![License](https://img.shields.io/github/issues/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) [![Stars](https://img.shields.io/github/stars/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) [![Stars](https://img.shields.io/github/forks/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) [![Forks](https://img.shields.io/github/issues/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) + +## 社区功能介绍 ++ [团队介绍]() ++ ** FlutterUI Demo ** ++ 文章类 + + Flutter系列教程 + + Flutter实战组件集锦 + + 其它推文 ++ 插件类 + + [Flutter-animation-set 动画插件](https://github.com/efoxTeam/flutter-animation-set) ++ [安卓包下载](https://github.com/efoxTeam/flutter-ui/releases/download/v1.0.2/app-release.apk) ++ 社区互动与交流 + + +## FlutterUI Demo预览 + + + + +### Flutter系列教程 +| 文章 | 文章状态 | 作者 | 变更日志 | +| --- | --- | --- | --- | +| | 进行中 | | | +| | 进行中 | | | +| | 进行中 | | | + + + +### Flutter实战组件集锦 +#### 弹层【集锦】 +| 文章 | 文章状态 | 作者 | 变更日志 | +| --- | --- | --- | --- | +| 透明弹窗背景 | 进行中 | | | +| 确认框 | 进行中 | | | +| 聊天输入框 | 进行中 | | | +| | 进行中 | | | +| | 进行中 | | | +| | 已完成 | | | + + +#### 列表【集锦】 +| 文章 | 文章状态 | 作者 | 变更日志 | +| --- | --- | --- | --- | +| 上下拉加载 | 进行中 | | | +| | 进行中 | | | + + +#### 列表【导航】 +| 文章 | 文章状态 | 作者 | 变更日志 | +| --- | --- | --- | --- | +| 顶部透明 | 进行中 | | | +| | 进行中 | | | + + +#### 列表【适配方案】 +| 文章 | 文章状态 | 作者 | 变更日志 | +| --- | --- | --- | --- | +| 适配iphonex底部 | 进行中 | | | +| | 进行中 | | | + +### 其它推文 +| 文章 | 文章状态 | 作者 | 变更日志 | +| --- | --- | --- | --- | +| [Flutter UI APP 低调上线](https://juejin.im/post/5c90514e6fb9a070c859029c) | 已完成 | [ken](https://github.com/ckken) | 2019/03/19 | +| [Flutter 接入 firebase 快速构建应用](https://juejin.im/post/5c90514e6fb9a070c859029c) | 已完成 | [ken](https://github.com/ckken) | 2019/03/19 | +| [Flutter UI 1.0.2落地与优化小结](https://juejin.im/post/5c95e691f265da610c06905c) | 已完成 | [ken](https://github.com/ckken) | 2019/03/23 | +| [Flutter 全局弹窗](https://juejin.im/post/5c9f2c37518825609415d11d) | 已完成 | [wanwu](https://github.com/wanwusangzhi) | 2019/03/30 | +| [Flutter UI使用Provide实现主题切换](https://juejin.im/post/5ca5e240f265da30c1725021) | 已完成 | [DIVINER-onlys](https://github.com/DIVINER-onlys) | 2019/04/04 | +| [Flutter provide ProvideMulti](https://juejin.im/post/5cc685835188252e8925f056) | 已完成 | [wanwu](https://github.com/wanwusangzhi) | 2019/03/23 | +| [以$t形式使用flutter多语言](https://juejin.im/post/5cdb8adee51d453acc60162c) | 已完成 | [DIVINER-onlys](https://github.com/DIVINER-onlys) | 2019/05/15 | +| [Flutter Provider 3.0实战教程](https://juejin.im/post/5d2c19c6e51d4558936aa11c) | 已完成 | [DIVINER-onlys](https://github.com/DIVINER-onlys) | 2019/07/15 | + ## apk 下载 ![安卓包下载](https://github.com/efoxTeam/flutter-ui/blob/master/readme/apk.png?raw=true) [安卓包下载](https://github.com/efoxTeam/flutter-ui/releases/download/v1.0.2/app-release.apk) -## Demo 预览 - - - ## 项目相关 + [apk包历史版本](https://github.com/efoxTeam/flutter-ui/releases) + [组件开发进度](readme/widget_progress.md) From be762bdd5bf944b656fa7e50d75b1cf891b3abdf Mon Sep 17 00:00:00 2001 From: wanwu <609780590@qq.com> Date: Fri, 16 Aug 2019 18:02:49 +0800 Subject: [PATCH 088/100] Update readme.md --- readme.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index c8461d5..7458481 100644 --- a/readme.md +++ b/readme.md @@ -1,13 +1,15 @@ # Flutter UI v1.0.2 - > YY Flutter 开发者社区 -> -[![License](https://img.shields.io/github/issues/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) [![Stars](https://img.shields.io/github/stars/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) [![Stars](https://img.shields.io/github/forks/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) [![Forks](https://img.shields.io/github/issues/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) + +[![License](https://img.shields.io/github/issues/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) +[![Stars](https://img.shields.io/github/stars/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) +[![Stars](https://img.shields.io/github/forks/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) +[![Forks](https://img.shields.io/github/issues/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) ## 社区功能介绍 + [团队介绍]() -+ ** FlutterUI Demo ** ++ FlutterUI Demo + 文章类 + Flutter系列教程 + Flutter实战组件集锦 @@ -22,7 +24,6 @@ - ### Flutter系列教程 | 文章 | 文章状态 | 作者 | 变更日志 | | --- | --- | --- | --- | From 7ed97a4d83e3466d981aef979e6592a57b601125 Mon Sep 17 00:00:00 2001 From: wanwu <609780590@qq.com> Date: Tue, 20 Aug 2019 09:58:50 +0800 Subject: [PATCH 089/100] Update readme.md --- readme.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/readme.md b/readme.md index 7458481..e970cc5 100644 --- a/readme.md +++ b/readme.md @@ -1,11 +1,11 @@ -# Flutter UI v1.0.2 +# Flutter UI v1.0.2 > YY Flutter 开发者社区 -[![License](https://img.shields.io/github/issues/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) -[![Stars](https://img.shields.io/github/stars/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) -[![Stars](https://img.shields.io/github/forks/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) -[![Forks](https://img.shields.io/github/issues/efoxTeam/flutter-ui.svg)](https://jitpack.io/#efoxTeam/flutter-ui) +[![License](https://img.shields.io/github/issues/YYFlutter/flutter-ui.svg)](https://jitpack.io/#YYFlutter/flutter-ui) +[![Stars](https://img.shields.io/github/stars/YYFlutter/flutter-ui.svg)](https://jitpack.io/#YYFlutter/flutter-ui) +[![Stars](https://img.shields.io/github/forks/YYFlutter/flutter-ui.svg)](https://jitpack.io/#YYFlutter/flutter-ui) +[![Forks](https://img.shields.io/github/issues/YYFlutter/flutter-ui.svg)](https://jitpack.io/#YYFlutter/flutter-ui) ## 社区功能介绍 + [团队介绍]() @@ -15,14 +15,14 @@ + Flutter实战组件集锦 + 其它推文 + 插件类 - + [Flutter-animation-set 动画插件](https://github.com/efoxTeam/flutter-animation-set) -+ [安卓包下载](https://github.com/efoxTeam/flutter-ui/releases/download/v1.0.2/app-release.apk) + + [Flutter-animation-set 动画插件](https://github.com/YYFlutter/flutter-animation-set) ++ [安卓包下载](https://github.com/YYFlutter/flutter-ui/releases/download/v1.0.2/app-release.apk) + 社区互动与交流 ## FlutterUI Demo预览 - - + + ### Flutter系列教程 | 文章 | 文章状态 | 作者 | 变更日志 | @@ -79,17 +79,17 @@ ## apk 下载 -![安卓包下载](https://github.com/efoxTeam/flutter-ui/blob/master/readme/apk.png?raw=true) -[安卓包下载](https://github.com/efoxTeam/flutter-ui/releases/download/v1.0.2/app-release.apk) +![安卓包下载](https://github.com/YYFlutter/flutter-ui/blob/master/readme/apk.png?raw=true) +[安卓包下载](https://github.com/YYFlutter/flutter-ui/releases/download/v1.0.2/app-release.apk) ## 项目相关 -+ [apk包历史版本](https://github.com/efoxTeam/flutter-ui/releases) ++ [apk包历史版本](https://github.com/YYFlutter/flutter-ui/releases) + [组件开发进度](readme/widget_progress.md) + [贡献PR参考](readme/pr.md) ## 项目交流 - + From 6c004825974ee4de8bab24f4e6b4719b4b7097af Mon Sep 17 00:00:00 2001 From: hkc Date: Tue, 20 Aug 2019 16:44:53 +0800 Subject: [PATCH 090/100] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E7=AB=A0?= =?UTF-8?q?=E5=8F=91=E7=89=88=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- article/.gitkeep | 0 article/preview/.gitkeep | 0 article/readme.md | 11 +++++++++++ 3 files changed, 11 insertions(+) create mode 100644 article/.gitkeep create mode 100644 article/preview/.gitkeep create mode 100644 article/readme.md diff --git a/article/.gitkeep b/article/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/article/preview/.gitkeep b/article/preview/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/article/readme.md b/article/readme.md new file mode 100644 index 0000000..721847b --- /dev/null +++ b/article/readme.md @@ -0,0 +1,11 @@ +## 文章投稿发版流程 + +> 这个目录是介绍Flutter系列文章的归档处 + +1. 首先要对外推广的文章,先放到preview文件夹下,发给大家审阅,如果继续修订,更新preview里面的文章即可 + +2. 审阅通过的文章,如果属于某个系列的,在article下新建文件夹,归档到里面,否则更新在article根文件夹即可 + +3. 完成第二步之后,更新根目录readme文章目录,同理,如果是系列文章,建议在readme里建list + +4. 更新完的目录后,可以同步更新到其他技术门户平台 \ No newline at end of file From ea103accdd2687c7cd115fbccac0376a26203871 Mon Sep 17 00:00:00 2001 From: hkc Date: Tue, 20 Aug 2019 16:55:43 +0800 Subject: [PATCH 091/100] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A0=E5=85=B3?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- article/.gitkeep | 0 article/preview/.gitkeep | 0 article/readme.md | 11 ----------- 3 files changed, 11 deletions(-) delete mode 100644 article/.gitkeep delete mode 100644 article/preview/.gitkeep delete mode 100644 article/readme.md diff --git a/article/.gitkeep b/article/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/article/preview/.gitkeep b/article/preview/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/article/readme.md b/article/readme.md deleted file mode 100644 index 721847b..0000000 --- a/article/readme.md +++ /dev/null @@ -1,11 +0,0 @@ -## 文章投稿发版流程 - -> 这个目录是介绍Flutter系列文章的归档处 - -1. 首先要对外推广的文章,先放到preview文件夹下,发给大家审阅,如果继续修订,更新preview里面的文章即可 - -2. 审阅通过的文章,如果属于某个系列的,在article下新建文件夹,归档到里面,否则更新在article根文件夹即可 - -3. 完成第二步之后,更新根目录readme文章目录,同理,如果是系列文章,建议在readme里建list - -4. 更新完的目录后,可以同步更新到其他技术门户平台 \ No newline at end of file From 3bf97d3118e1485c785d398ff8137eaca835a6b1 Mon Sep 17 00:00:00 2001 From: KenZR Date: Thu, 29 Aug 2019 19:00:36 +0800 Subject: [PATCH 092/100] =?UTF-8?q?feat:=E9=87=8D=E6=9E=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=B1=82=20=E5=A2=9E=E5=8A=A0=20provider=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/components/widget_comp.dart | 8 +- lib/controller/index.dart | 12 +- lib/http/loading.dart | 4 +- lib/main.dart | 60 +++++----- lib/page/comment/details.dart | 21 ++-- lib/page/comment/index.dart | 4 +- lib/page/component/tabs.dart | 1 + lib/page/home.dart | 24 ++-- lib/store/index.dart | 111 +++++------------- lib/store/index_for_provide.dart | 97 +++++++++++++++ lib/utils/appVersion.dart | 1 + .../scrollview/gridview/demo_custom.dart | 1 + pubspec.yaml | 1 + 13 files changed, 196 insertions(+), 149 deletions(-) create mode 100644 lib/store/index_for_provide.dart diff --git a/lib/components/widget_comp.dart b/lib/components/widget_comp.dart index 1845fb6..8eb4ca8 100644 --- a/lib/components/widget_comp.dart +++ b/lib/components/widget_comp.dart @@ -75,10 +75,10 @@ class IndexState extends State { this._bodyList.length = 0; String mdText = await this.getMdFile(widget.mdUrl); String nameKey = AuthorList.list[widget.title]; - if (nameKey != null) { + /*if (nameKey != null) { this._bodyList.add(authorTile(nameKey)); this._bodyList.add(Divider()); - } + }*/ print('文档完成长度:${mdText.length}'); if (mdText.length > 30) { this._bodyList.add(await markdown_comp.Index(mdText)); @@ -152,7 +152,7 @@ class IndexState extends State { getActions(context) { return [ - IconButton( + /*IconButton( color: Color(AppTheme.blackColor), icon: Icon( Icons.code @@ -172,7 +172,7 @@ class IndexState extends State { '/webview?title=${widget.title}&url=${Uri.encodeComponent(widget.originCodeUrl)}', ); }, - ), + ),*/ IconButton( icon: Icon(Icons.share), color: Color(AppTheme.blackColor), diff --git a/lib/controller/index.dart b/lib/controller/index.dart index e3ad404..6e4b37d 100644 --- a/lib/controller/index.dart +++ b/lib/controller/index.dart @@ -5,15 +5,15 @@ import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; void initState() async { // 获取版本号 - Store.valueNotCtx().getAppVersion(); + Store.value().getAppVersion(); // 登录 - Store.valueNotCtx().getLocalUserInfo().then((res) { + /*Store.value().getLocalUserInfo().then((res) { if (res) { - Store.valueNotCtx().getUserStar(); + Store.value().getUserStar(); } - }); - Store.valueNotCtx().getFlutterUIStar(); + });*/ + // Store.value().getFlutterUIStar(); Future.delayed(Duration(seconds: 3), () { - AppVersion().check(Store.widgetCtx); + AppVersion().check(Store.context); }); } diff --git a/lib/http/loading.dart b/lib/http/loading.dart index 55e8074..ecac0b3 100644 --- a/lib/http/loading.dart +++ b/lib/http/loading.dart @@ -16,7 +16,7 @@ void beforeRequest(uri, Map options) { void afterResponse(uri, Map options) { dict.remove(uri); if (dict.length == 0 && loading == true) { - Navigator.of(Store.widgetCtx, rootNavigator: true).pop('close dialog'); + Navigator.of(Store.context, rootNavigator: true).pop('close dialog'); loading = false; } } @@ -31,7 +31,7 @@ void showAppLoading(Map options) { 'text': options['text'] ?? 'loading...' }; showDialog( - context: Store.widgetCtx, + context: Store.context, builder: (context) { return LoadingDialog(text: options['text']); }, diff --git a/lib/main.dart b/lib/main.dart index 895c764..17a6c55 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,8 +3,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; //语言包 import 'package:efox_flutter/lang/index.dart' show AppLocalizationsDelegate, AppLocalizations; import 'package:efox_flutter/lang/config.dart' show ConfigLanguage; -import 'package:efox_flutter/store/index.dart' - show Store, ConfigModel; //引用Store 层 +import 'package:efox_flutter/store/index.dart'; //引用Store 层 import 'package:efox_flutter/router/index.dart' show FluroRouter; //路由 import 'package:efox_flutter/config/theme.dart' show AppTheme; //主题 import 'package:efox_flutter/utils/analytics.dart' as Analytics; //统计 @@ -26,40 +25,39 @@ class MainAppState extends State { //实例化多语言 super.initState(); _delegate = AppLocalizationsDelegate(); - Store.setStoreCtx(context); // 初始化数据层 + + Future.delayed(Duration.zero, () async { + Store.value(context).getTheme(); + }); } @override Widget build(BuildContext context) { - Store.value(context).getTheme(); - - return Store.connect( - builder: (context, child, model) { - return MaterialApp( - localeResolutionCallback: (deviceLocale, supportedLocales) { - print( - 'deviceLocale=$deviceLocale supportedLocales=$supportedLocales'); - Locale _locale = supportedLocales.contains(deviceLocale) - ? deviceLocale - : Locale('zh'); - return _locale; - }, - onGenerateTitle: (ctx) { - // 设置多语言代理 - AppLocalizations.setProxy(setState, _delegate); - return 'flutter'; - }, - localizationsDelegates: [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - _delegate, - ], - supportedLocales: ConfigLanguage.supportedLocales, - theme: AppTheme.getThemeData(model.theme), - onGenerateRoute: FluroRouter.router.generator, - navigatorObservers: [Analytics.observer], - ); + Store.of(context); + print('Store.value(context)=${Store.value(context).theme}'); + return MaterialApp( + localeResolutionCallback: (deviceLocale, supportedLocales) { + print( + 'deviceLocale=$deviceLocale supportedLocales=$supportedLocales'); + Locale _locale = supportedLocales.contains(deviceLocale) + ? deviceLocale + : Locale('zh'); + return _locale; + }, + onGenerateTitle: (ctx) { + // 设置多语言代理 + AppLocalizations.setProxy(setState, _delegate); + return 'flutter'; }, + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + _delegate, + ], + supportedLocales: ConfigLanguage.supportedLocales, + theme: AppTheme.getThemeData(Store.value(context).theme), + onGenerateRoute: FluroRouter.router.generator, + navigatorObservers: [Analytics.observer], ); } } diff --git a/lib/page/comment/details.dart b/lib/page/comment/details.dart index 424a68b..30fb8fa 100644 --- a/lib/page/comment/details.dart +++ b/lib/page/comment/details.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -import 'package:efox_flutter/store/index.dart' show Store, UserModel; +import 'package:efox_flutter/store/index.dart'; import 'package:efox_flutter/store/objects/flutter_ui_issues.dart' show IssuesContent; import 'package:efox_flutter/store/objects/issues_comment.dart' show IssuesDetails; import 'package:efox_flutter/page/app_login/index.dart' as LoginIndex; class Index extends StatefulWidget { - int indexes; + final int indexes; Index({ Key key, @required this.indexes }):super(key: key); @override _IndexState createState() => _IndexState(); @@ -56,8 +56,8 @@ class _IndexState extends State { } Widget _IssueContent (BuildContext context) { - return Store.connect( - builder: (context, child, model) { + return Consumer( + builder: (context, model,child) { IssuesContent issuesContent = model.flutter_ui_issues.issuesContent[widget.indexes]; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -98,6 +98,7 @@ class _IndexState extends State { Widget _CommentContent (BuildContext context) { return FutureBuilder( future: _getComment, + // ignore: missing_return builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Container( @@ -108,8 +109,8 @@ class _IndexState extends State { ); } else if(snapshot.connectionState == ConnectionState.done) { if (snapshot.hasData) { - return Store.connect( - builder: (context, child, model) { + return Consumer( + builder: (context, model,child) { List items = []; for(var issuesDetails in model.issues_comment.issuesDetails) { items.add(_CommentContentItem(context, issuesDetails)); @@ -130,8 +131,8 @@ class _IndexState extends State { } Future _getIssueComment(BuildContext context) async { - IssuesContent issuesContent = Store.valueNotCtx().flutter_ui_issues.issuesContent[widget.indexes]; - await Store.valueNotCtx().getIssueComment(issuesContent.number); + IssuesContent issuesContent = Store.value(context).flutter_ui_issues.issuesContent[widget.indexes]; + await Store.value(context).getIssueComment(issuesContent.number); return 'end'; } @@ -174,8 +175,8 @@ class _IndexState extends State { flex: 1, child: _InputBox(), ), - Store.connect( - builder: (context, child, model) { + Consumer( + builder: (context, model,child) { IssuesContent issuesContent = model.flutter_ui_issues.issuesContent[widget.indexes]; return GestureDetector( onTap: () async { diff --git a/lib/page/comment/index.dart b/lib/page/comment/index.dart index 3a07402..c6e70a4 100644 --- a/lib/page/comment/index.dart +++ b/lib/page/comment/index.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:efox_flutter/lang/index.dart' show AppLocalizations; -import 'package:efox_flutter/store/index.dart' show Store, UserModel; +import 'package:efox_flutter/store/index.dart'; import 'package:efox_flutter/store/objects/flutter_ui_issues.dart' show IssuesContent; import 'package:efox_flutter/router/index.dart' show FluroRouter; @@ -26,7 +26,7 @@ class _IndexState extends State { } Widget _CommentList(BuildContext context) { - return Store.connect(builder: (context, child, model) { + return Consumer(builder: (context, model,child) { if (model.flutter_ui_issues != null && model.flutter_ui_issues.issuesContent != null && model.flutter_ui_issues.issuesContent.length != 0) { diff --git a/lib/page/component/tabs.dart b/lib/page/component/tabs.dart index a64bf2e..84f9fbc 100644 --- a/lib/page/component/tabs.dart +++ b/lib/page/component/tabs.dart @@ -38,6 +38,7 @@ class _IndexState extends State final GlobalKey _scaffoldKey = new GlobalKey(); + // ignore: must_call_super Widget build(BuildContext context) { return Scaffold( key: _scaffoldKey, diff --git a/lib/page/home.dart b/lib/page/home.dart index 91a88ee..3e4da35 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -8,7 +8,7 @@ import 'app_login/index.dart' as LoginIndex; import 'comment/index.dart' as CommentIndex; import 'library/index.dart' as LibraryIndex; -import 'package:efox_flutter/store/index.dart' show Store, UserModel; +import 'package:efox_flutter/store/index.dart'; class Index extends StatefulWidget { @override @@ -46,12 +46,12 @@ class _IndexState extends State { BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_component')), icon: Icon(Icons.dashboard)), - BottomNavigationBarItem( +/* BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_comment')), icon: Icon(Icons.comment)), BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_library')), - icon: Icon(Icons.library_add)), + icon: Icon(Icons.library_add)),*/ BottomNavigationBarItem( title: Text(AppLocalizations.$t('title_my')), icon: Icon(Icons.person_outline)), @@ -59,9 +59,9 @@ class _IndexState extends State { // type: BottomNavigationBarType.fixed, currentIndex: _currentIndex, onTap: (int index) { - if (index == 1 && _currentIndex != index) { + /* if (index == 1 && _currentIndex != index) { Store.value(context).getIssueFlutterUI(); - } + }*/ _pageController.jumpToPage(index); }, ), @@ -104,7 +104,7 @@ class _IndexState extends State { renderDrawer() { print('renderDrawer $context'); return Drawer( - child: Store.connect(builder: (context, child, model) { + child: Consumer(builder: (context, model,child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -145,7 +145,7 @@ class _IndexState extends State { } Widget _floatingActionButton(context) { - return Store.connect(builder: (context, child, model) { + return Consumer(builder: (context, model,child) { return FloatingActionButton( backgroundColor: Theme.of(context).primaryColor, onPressed: () { @@ -184,12 +184,12 @@ class _IndexState extends State { @override Widget build(BuildContext context) { - Store.setWidgetCtx(context); // 初始化scaffold的上下文作为全局上下文,提供弹窗等使用。 + // Store.setWidgetCtx(context); // 初始化scaffold的上下文作为全局上下文,提供弹窗等使用。 return Scaffold( drawer: renderDrawer(), bottomNavigationBar: _bottomNavigationBar(), - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - floatingActionButton: _floatingActionButton(context), + // floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + // floatingActionButton: _floatingActionButton(context), body: PageView( controller: _pageController, physics: NeverScrollableScrollPhysics(), @@ -200,8 +200,8 @@ class _IndexState extends State { }, children: [ TabIndex.Index(), - CommentIndex.Index(), - LibraryIndex.Index(), + // CommentIndex.Index(), + //LibraryIndex.Index(), MyIndex.Index(), ], ), diff --git a/lib/store/index.dart b/lib/store/index.dart index 4872213..2667c0b 100644 --- a/lib/store/index.dart +++ b/lib/store/index.dart @@ -1,97 +1,44 @@ -import 'package:flutter/material.dart' show StreamBuilder; -import 'package:provide/provide.dart' - show - Provider, - Provide, - ProviderNode, - Providers, - ProvideMulti, - ProviderScope; -export 'package:provide/provide.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart' + show ChangeNotifierProvider, MultiProvider, Consumer, Provider; +export 'package:provider/provider.dart'; +// import './models/config_state_model.dart' show ConfigModel; import './models/user_model.dart' show UserModel; import './models/author_state_model.dart' show AuthorModel; export './models/config_state_model.dart' show ConfigModel; export './models/user_model.dart' show UserModel; export './models/author_state_model.dart' show AuthorModel; -import 'package:flutter/material.dart'; class Store { - static dynamic storeCtx = null; - static dynamic widgetCtx = null; - static init({model, child, dispose = true}) { - final providers = Providers() - ..provide(Provider.value(ConfigModel())) - ..provide(Provider.value(UserModel())) - ..provide(Provider.value(AuthorModel())); - - return ProviderNode( - child: child, - providers: providers, - dispose: dispose, - ); - } - /** - * 设置数据层上下文 - */ - static setStoreCtx(context) { - storeCtx = context; - } - - /** - * 设置Widget上下文 - */ - static setWidgetCtx(context) { - widgetCtx = context; - } - - /** - * 获取 - */ - static T valueNotCtx() { - return Provide.value(storeCtx); - } - - /** - * 根据 Context 获取 - */ - static T value(context, {scope}) { - return Provide.value(context, scope: scope); - } - - /** - * 监听 - */ - static connect({builder, child, scope}) { - return Provide( - builder: builder, + static BuildContext context; + static of(BuildContext context){ + Store.context ??= context; + return context; + } + static init({context, child}) { + Store.context ??= context; + return MultiProvider( child: child, - scope: scope, + providers: [ + ChangeNotifierProvider(builder: (_) => ConfigModel()), + ChangeNotifierProvider(builder: (_) => UserModel()), + ChangeNotifierProvider(builder: (_) => AuthorModel()), + ], ); } - /** - * 通过流的方式 监听 - */ - static stream({builder, model, context}) { - return StreamBuilder( - initialData: model, - stream: Provide.stream(context), - builder: builder); + static T value([BuildContext context]) { + context ??= Store.context; + return Provider.of(context); } +} - /** - * 链接多个类型 - */ - static multi( - {builder, - child, - List requestedValues, - Map> requestedScopedValues}) { - return ProvideMulti( - builder: builder, - child: child, - requestedValues: requestedValues, - requestedScopedValues: requestedScopedValues); +class StoreProvider extends StatelessWidget { + final Widget child; + StoreProvider({this.child}); + @override + Widget build(BuildContext context) { + return Store.init(child: child,context: context); } -} +} \ No newline at end of file diff --git a/lib/store/index_for_provide.dart b/lib/store/index_for_provide.dart new file mode 100644 index 0000000..4872213 --- /dev/null +++ b/lib/store/index_for_provide.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart' show StreamBuilder; +import 'package:provide/provide.dart' + show + Provider, + Provide, + ProviderNode, + Providers, + ProvideMulti, + ProviderScope; +export 'package:provide/provide.dart'; +import './models/config_state_model.dart' show ConfigModel; +import './models/user_model.dart' show UserModel; +import './models/author_state_model.dart' show AuthorModel; +export './models/config_state_model.dart' show ConfigModel; +export './models/user_model.dart' show UserModel; +export './models/author_state_model.dart' show AuthorModel; +import 'package:flutter/material.dart'; + +class Store { + static dynamic storeCtx = null; + static dynamic widgetCtx = null; + static init({model, child, dispose = true}) { + final providers = Providers() + ..provide(Provider.value(ConfigModel())) + ..provide(Provider.value(UserModel())) + ..provide(Provider.value(AuthorModel())); + + return ProviderNode( + child: child, + providers: providers, + dispose: dispose, + ); + } + /** + * 设置数据层上下文 + */ + static setStoreCtx(context) { + storeCtx = context; + } + + /** + * 设置Widget上下文 + */ + static setWidgetCtx(context) { + widgetCtx = context; + } + + /** + * 获取 + */ + static T valueNotCtx() { + return Provide.value(storeCtx); + } + + /** + * 根据 Context 获取 + */ + static T value(context, {scope}) { + return Provide.value(context, scope: scope); + } + + /** + * 监听 + */ + static connect({builder, child, scope}) { + return Provide( + builder: builder, + child: child, + scope: scope, + ); + } + + /** + * 通过流的方式 监听 + */ + static stream({builder, model, context}) { + return StreamBuilder( + initialData: model, + stream: Provide.stream(context), + builder: builder); + } + + /** + * 链接多个类型 + */ + static multi( + {builder, + child, + List requestedValues, + Map> requestedScopedValues}) { + return ProvideMulti( + builder: builder, + child: child, + requestedValues: requestedValues, + requestedScopedValues: requestedScopedValues); + } +} diff --git a/lib/utils/appVersion.dart b/lib/utils/appVersion.dart index 6a894b7..eeb865b 100644 --- a/lib/utils/appVersion.dart +++ b/lib/utils/appVersion.dart @@ -61,6 +61,7 @@ class AppVersion { url: 'https://raw.githubusercontent.com/efoxTeam/flutter-ui/master/version.json', ); + // ignore: missing_return return await response.then((resp) { if (resp.data != null) { var data = json.decode(resp.data); diff --git a/lib/widget/scrollview/gridview/demo_custom.dart b/lib/widget/scrollview/gridview/demo_custom.dart index dd9f239..f677390 100644 --- a/lib/widget/scrollview/gridview/demo_custom.dart +++ b/lib/widget/scrollview/gridview/demo_custom.dart @@ -23,6 +23,7 @@ class Index extends StatelessWidget { // 滚动时回调函数 semanticIndexCallback: (widget, index) { print('index $index'); + return index; }, ), // 数量滚滚动限制,类似GridView.count diff --git a/pubspec.yaml b/pubspec.yaml index 8fa2882..6da9c2e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: pull_to_refresh: ^1.1.6 #加载更多 shared_preferences: ^0.4.2 #简单数据存储 provide: ^1.0.2 #数据管理层 + provider: ^3.1.0 dio: ^2.0.14 cached_network_image: ^0.5.1 intl: ^0.15.7 From b6d1dbcfd70cf31078f69f5a246d48ecc2e140c1 Mon Sep 17 00:00:00 2001 From: KenZR Date: Thu, 29 Aug 2019 19:01:47 +0800 Subject: [PATCH 093/100] =?UTF-8?q?feat:=E9=87=8D=E6=9E=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=B1=82=20=E5=A2=9E=E5=8A=A0=20provider=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 110c723..565be38 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -45,8 +45,6 @@ 15BE2897D1518D9BDD5AF970 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; - 3DB87B9B20AEA710C58FBCC8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 6F6754CA16FD7E3C9E4FCEB3 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -60,7 +58,6 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BEBBA521223FEA6600583D52 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; - C1D6F379F6B3C85C2CC34999 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; D7BE9010C4F7FF033BA299B7 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; FDCB5E5FC17434D1FF8247D2 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -192,6 +189,7 @@ 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; DevelopmentTeam = 5K9P57YFSL; + ProvisioningStyle = Automatic; }; }; }; @@ -283,7 +281,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/flutter_downloader/FlutterDownloaderDatabase.bundle", ); name = "[CP] Copy Pods Resources"; @@ -292,7 +290,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; showEnvVarsInLog = 0; }; EB6D2D2E068567EE260A8427 /* [CP] Embed Pods Frameworks */ = { @@ -301,7 +299,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; @@ -310,7 +308,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -401,6 +399,8 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 5K9P57YFSL; ENABLE_BITCODE = NO; @@ -414,8 +414,9 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.flutter.ui; + PRODUCT_BUNDLE_IDENTIFIER = com.flutterken.ui; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -527,6 +528,8 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 5K9P57YFSL; ENABLE_BITCODE = NO; @@ -540,8 +543,9 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.flutter.ui; + PRODUCT_BUNDLE_IDENTIFIER = com.flutterken.ui; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -551,6 +555,8 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 5K9P57YFSL; ENABLE_BITCODE = NO; @@ -564,8 +570,9 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.flutter.ui; + PRODUCT_BUNDLE_IDENTIFIER = com.flutterken.ui; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; From 1589620171c1dfcd32b73a5037a66a93bdb80c6e Mon Sep 17 00:00:00 2001 From: KenZR Date: Thu, 29 Aug 2019 19:06:16 +0800 Subject: [PATCH 094/100] =?UTF-8?q?feat:=E9=87=8D=E6=9E=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=B1=82=20=E5=A2=9E=E5=8A=A0=20provider=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/components/widget_comp.dart | 6 +++--- lib/page/mine/index.dart | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/components/widget_comp.dart b/lib/components/widget_comp.dart index 089ada8..be5db94 100644 --- a/lib/components/widget_comp.dart +++ b/lib/components/widget_comp.dart @@ -118,13 +118,13 @@ class IndexState extends State { } showCode(context) async { - if (Store.value(context).isPro) { + /*if (Store.value(context).isPro) { // 线上文档 FluroRouter.router.navigateTo( context, 'webview?title=${widget.title}&url=${Uri.encodeComponent(Store.value(context).env.githubAssetOrigin+widget.mdUrl)}' ); - } else { + } else {*/ // 加载本地 String mdStr = await AssetUtils.readLocaleFile(widget.mdUrl); Navigator.of(context).push( @@ -137,7 +137,7 @@ class IndexState extends State { ); }) ); - } + //} } Future getMdFile(url) async { diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index e60d107..5f5a59e 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -108,7 +108,7 @@ class _IndexState extends State { ) ], ), - Divider( + /* Divider( color: Color(AppTheme.lineColor), ), ListTile( @@ -119,7 +119,7 @@ class _IndexState extends State { title: Text(Store.value(context).isPro ? AppLocalizations.$t('common_mine_1.doc_online') : AppLocalizations.$t('common_mine_1.doc_offline')), - ), + ),*/ Divider( color: Color(AppTheme.lineColor), ), From ce0c85459089ccb4b029b318ec3d94ec0198ebff Mon Sep 17 00:00:00 2001 From: KenZR Date: Fri, 30 Aug 2019 18:13:37 +0800 Subject: [PATCH 095/100] =?UTF-8?q?feat:=E9=87=8D=E6=9E=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=B1=82=20=E5=A2=9E=E5=8A=A0=20provider=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/config/theme.dart | 2 +- lib/main.dart | 62 ++++++++++++++++++++-------------- lib/package/README.md | 2 ++ lib/package/router/router.dart | 0 lib/page/home.dart | 1 + lib/plugin/README.md | 2 ++ lib/store/index.dart | 18 ++++------ pubspec.yaml | 1 + 8 files changed, 50 insertions(+), 38 deletions(-) create mode 100644 lib/package/README.md create mode 100644 lib/package/router/router.dart create mode 100644 lib/plugin/README.md diff --git a/lib/config/theme.dart b/lib/config/theme.dart index 0582100..4d34214 100644 --- a/lib/config/theme.dart +++ b/lib/config/theme.dart @@ -15,7 +15,7 @@ class AppTheme { static int blackColor = 0xFF000000; static int lineColor = 0xFFEEEEEE; static getThemeData(String theme) { - //print('==================================getThemeData=$theme'); + print('==================================getThemeData=$theme'); mainColor = materialColor[theme]; ThemeData themData = ThemeData( textTheme: TextTheme( diff --git a/lib/main.dart b/lib/main.dart index 17a6c55..195b3ac 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,9 @@ import 'package:efox_flutter/store/index.dart'; //引用Store 层 import 'package:efox_flutter/router/index.dart' show FluroRouter; //路由 import 'package:efox_flutter/config/theme.dart' show AppTheme; //主题 import 'package:efox_flutter/utils/analytics.dart' as Analytics; //统计 +import 'package:oktoast/oktoast.dart' show OKToast; +import 'package:efox_flutter/page/home.dart' as HomePage; + // import './mock/index.dart' as TestCase; class MainApp extends StatefulWidget { MainApp() { @@ -17,6 +20,7 @@ class MainApp extends StatefulWidget { @override MainAppState createState() => MainAppState(); } + class MainAppState extends State { // 定义全局 语言代理 AppLocalizationsDelegate _delegate; @@ -27,39 +31,47 @@ class MainAppState extends State { _delegate = AppLocalizationsDelegate(); Future.delayed(Duration.zero, () async { - Store.value(context).getTheme(); + Store.value().getTheme(); }); } @override Widget build(BuildContext context) { Store.of(context); - print('Store.value(context)=${Store.value(context).theme}'); - return MaterialApp( - localeResolutionCallback: (deviceLocale, supportedLocales) { - print( - 'deviceLocale=$deviceLocale supportedLocales=$supportedLocales'); - Locale _locale = supportedLocales.contains(deviceLocale) - ? deviceLocale - : Locale('zh'); - return _locale; + return Consumer( + builder: (context, configModel, child) { + return MaterialApp( + localeResolutionCallback: (deviceLocale, supportedLocales) { + print( + 'deviceLocale=$deviceLocale supportedLocales=$supportedLocales context=$context'); + Locale _locale = supportedLocales.contains(deviceLocale) + ? deviceLocale + : Locale('zh'); + return _locale; + }, + onGenerateTitle: (ctx) { + // 设置多语言代理 + AppLocalizations.setProxy(setState, _delegate); + return 'flutter'; + }, + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + _delegate, + ], + supportedLocales: ConfigLanguage.supportedLocales, + theme: AppTheme.getThemeData(configModel.theme), + // onGenerateRoute: FluroRouter.router.generator, + home: HomePage.Index(), + navigatorObservers: [Analytics.observer], + ); }, - onGenerateTitle: (ctx) { - // 设置多语言代理 - AppLocalizations.setProxy(setState, _delegate); - return 'flutter'; - }, - localizationsDelegates: [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - _delegate, - ], - supportedLocales: ConfigLanguage.supportedLocales, - theme: AppTheme.getThemeData(Store.value(context).theme), - onGenerateRoute: FluroRouter.router.generator, - navigatorObservers: [Analytics.observer], ); } } -void main() => runApp(Store.init(child: MainApp())); +void main() => runApp( + Store.init( + child: MainApp(), + ), + ); diff --git a/lib/package/README.md b/lib/package/README.md new file mode 100644 index 0000000..21f6e02 --- /dev/null +++ b/lib/package/README.md @@ -0,0 +1,2 @@ +# Flutter Packages for FLUTTER UI APP +> packages 为纯flutter 组件,沉淀通用组件,通用方法,通用模块 \ No newline at end of file diff --git a/lib/package/router/router.dart b/lib/package/router/router.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/page/home.dart b/lib/page/home.dart index 3e4da35..53f7add 100644 --- a/lib/page/home.dart +++ b/lib/page/home.dart @@ -105,6 +105,7 @@ class _IndexState extends State { print('renderDrawer $context'); return Drawer( child: Consumer(builder: (context, model,child) { + print('render model $model'); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/plugin/README.md b/lib/plugin/README.md new file mode 100644 index 0000000..88bf7f9 --- /dev/null +++ b/lib/plugin/README.md @@ -0,0 +1,2 @@ +# Flutter Plugin for FLUTTER UI APP +> plugin 为混编 flutter 组件,沉淀通用组件,通用方法,通用模块 \ No newline at end of file diff --git a/lib/store/index.dart b/lib/store/index.dart index 2667c0b..6f1130e 100644 --- a/lib/store/index.dart +++ b/lib/store/index.dart @@ -12,12 +12,12 @@ export './models/author_state_model.dart' show AuthorModel; class Store { static BuildContext context; - static of(BuildContext context){ + static of(BuildContext context) { Store.context ??= context; return context; } - static init({context, child}) { - Store.context ??= context; + + static init({child}) { return MultiProvider( child: child, providers: [ @@ -30,15 +30,9 @@ class Store { static T value([BuildContext context]) { context ??= Store.context; - return Provider.of(context); + return Provider.of(context); } + + } -class StoreProvider extends StatelessWidget { - final Widget child; - StoreProvider({this.child}); - @override - Widget build(BuildContext context) { - return Store.init(child: child,context: context); - } -} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 6da9c2e..80c04da 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,7 @@ dependencies: flutter_downloader: ^1.1.6 open_file: ^2.0.1+1 permission_handler: ^3.0.0 + oktoast: ^2.2.0 dev_dependencies: flutter_test: From f376fe9ae1ae0bd268310f92bdc4e8919216b738 Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 3 Sep 2019 15:34:57 +0800 Subject: [PATCH 096/100] =?UTF-8?q?feat:provider=20=E6=9B=BF=E6=8D=A2=20pr?= =?UTF-8?q?ovide=201.0.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 4 +-- pubspec.yaml | 2 +- readme.md | 86 +++------------------------------------------------ version.json | 2 +- 4 files changed, 8 insertions(+), 86 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 195b3ac..7286253 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -61,8 +61,8 @@ class MainAppState extends State { ], supportedLocales: ConfigLanguage.supportedLocales, theme: AppTheme.getThemeData(configModel.theme), - // onGenerateRoute: FluroRouter.router.generator, - home: HomePage.Index(), + onGenerateRoute: FluroRouter.router.generator, + // home: HomePage.Index(), navigatorObservers: [Analytics.observer], ); }, diff --git a/pubspec.yaml b/pubspec.yaml index 80c04da..90e4af4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ description: A new Flutter project. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # Read more about versioning at semver.org. -version: 1.0.2+1 +version: 1.0.3+1 environment: sdk: ">=2.1.0 <3.0.0" diff --git a/readme.md b/readme.md index e970cc5..a9042ee 100644 --- a/readme.md +++ b/readme.md @@ -1,88 +1,10 @@ -# Flutter UI v1.0.2 +# Flutter UI v1.0.3 +[项目正筹划新一轮更新欢迎建议OR提ISSUE](https://github.com/YYFlutter/flutter-ui/issues/55) -> YY Flutter 开发者社区 - -[![License](https://img.shields.io/github/issues/YYFlutter/flutter-ui.svg)](https://jitpack.io/#YYFlutter/flutter-ui) -[![Stars](https://img.shields.io/github/stars/YYFlutter/flutter-ui.svg)](https://jitpack.io/#YYFlutter/flutter-ui) -[![Stars](https://img.shields.io/github/forks/YYFlutter/flutter-ui.svg)](https://jitpack.io/#YYFlutter/flutter-ui) -[![Forks](https://img.shields.io/github/issues/YYFlutter/flutter-ui.svg)](https://jitpack.io/#YYFlutter/flutter-ui) - -## 社区功能介绍 -+ [团队介绍]() -+ FlutterUI Demo -+ 文章类 - + Flutter系列教程 - + Flutter实战组件集锦 - + 其它推文 -+ 插件类 - + [Flutter-animation-set 动画插件](https://github.com/YYFlutter/flutter-animation-set) -+ [安卓包下载](https://github.com/YYFlutter/flutter-ui/releases/download/v1.0.2/app-release.apk) -+ 社区互动与交流 - - -## FlutterUI Demo预览 - - - -### Flutter系列教程 -| 文章 | 文章状态 | 作者 | 变更日志 | -| --- | --- | --- | --- | -| | 进行中 | | | -| | 进行中 | | | -| | 进行中 | | | - - - -### Flutter实战组件集锦 -#### 弹层【集锦】 -| 文章 | 文章状态 | 作者 | 变更日志 | -| --- | --- | --- | --- | -| 透明弹窗背景 | 进行中 | | | -| 确认框 | 进行中 | | | -| 聊天输入框 | 进行中 | | | -| | 进行中 | | | -| | 进行中 | | | -| | 已完成 | | | - - -#### 列表【集锦】 -| 文章 | 文章状态 | 作者 | 变更日志 | -| --- | --- | --- | --- | -| 上下拉加载 | 进行中 | | | -| | 进行中 | | | - - -#### 列表【导航】 -| 文章 | 文章状态 | 作者 | 变更日志 | -| --- | --- | --- | --- | -| 顶部透明 | 进行中 | | | -| | 进行中 | | | - - -#### 列表【适配方案】 -| 文章 | 文章状态 | 作者 | 变更日志 | -| --- | --- | --- | --- | -| 适配iphonex底部 | 进行中 | | | -| | 进行中 | | | - -### 其它推文 -| 文章 | 文章状态 | 作者 | 变更日志 | -| --- | --- | --- | --- | -| [Flutter UI APP 低调上线](https://juejin.im/post/5c90514e6fb9a070c859029c) | 已完成 | [ken](https://github.com/ckken) | 2019/03/19 | -| [Flutter 接入 firebase 快速构建应用](https://juejin.im/post/5c90514e6fb9a070c859029c) | 已完成 | [ken](https://github.com/ckken) | 2019/03/19 | -| [Flutter UI 1.0.2落地与优化小结](https://juejin.im/post/5c95e691f265da610c06905c) | 已完成 | [ken](https://github.com/ckken) | 2019/03/23 | -| [Flutter 全局弹窗](https://juejin.im/post/5c9f2c37518825609415d11d) | 已完成 | [wanwu](https://github.com/wanwusangzhi) | 2019/03/30 | -| [Flutter UI使用Provide实现主题切换](https://juejin.im/post/5ca5e240f265da30c1725021) | 已完成 | [DIVINER-onlys](https://github.com/DIVINER-onlys) | 2019/04/04 | -| [Flutter provide ProvideMulti](https://juejin.im/post/5cc685835188252e8925f056) | 已完成 | [wanwu](https://github.com/wanwusangzhi) | 2019/03/23 | -| [以$t形式使用flutter多语言](https://juejin.im/post/5cdb8adee51d453acc60162c) | 已完成 | [DIVINER-onlys](https://github.com/DIVINER-onlys) | 2019/05/15 | -| [Flutter Provider 3.0实战教程](https://juejin.im/post/5d2c19c6e51d4558936aa11c) | 已完成 | [DIVINER-onlys](https://github.com/DIVINER-onlys) | 2019/07/15 | - - -## apk 下载 -![安卓包下载](https://github.com/YYFlutter/flutter-ui/blob/master/readme/apk.png?raw=true) -[安卓包下载](https://github.com/YYFlutter/flutter-ui/releases/download/v1.0.2/app-release.apk) ## 项目相关 ++ [项目官方文章](https://github.com/YYFlutter/flutter-article) ++ [项目开发规范](https://github.com/YYFlutter/flutter-article/blob/master/lint/v1.0.md) + [apk包历史版本](https://github.com/YYFlutter/flutter-ui/releases) + [组件开发进度](readme/widget_progress.md) + [贡献PR参考](readme/pr.md) diff --git a/version.json b/version.json index 84184c0..bcfa2c4 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"1.0.2"} +{"version":"1.0.3"} From 3b1badf18dfe96ffdb30834d97e63e830333125f Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 3 Sep 2019 16:54:44 +0800 Subject: [PATCH 097/100] =?UTF-8?q?feat:provider=20=E6=9B=BF=E6=8D=A2=20pr?= =?UTF-8?q?ovide=201.0.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 14 ++++++++++++-- res/values/strings_en.arb | 0 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 res/values/strings_en.arb diff --git a/readme.md b/readme.md index a9042ee..9fa29f7 100644 --- a/readme.md +++ b/readme.md @@ -1,14 +1,24 @@ # Flutter UI v1.0.3 -[项目正筹划新一轮更新欢迎建议OR提ISSUE](https://github.com/YYFlutter/flutter-ui/issues/55) ++ [项目正筹划新一轮更新欢迎建议OR提ISSUE](https://github.com/YYFlutter/flutter-ui/issues/55) ++ [点击下载](https://github.com/YYFlutter/flutter-ui/releases/download/v1.0.3/app-release.apk) ## 项目相关 -+ [项目官方文章](https://github.com/YYFlutter/flutter-article) + [项目开发规范](https://github.com/YYFlutter/flutter-article/blob/master/lint/v1.0.md) + [apk包历史版本](https://github.com/YYFlutter/flutter-ui/releases) + [组件开发进度](readme/widget_progress.md) + [贡献PR参考](readme/pr.md) +### Flutter系列教程入门文章 +[项目官方文章](https://github.com/YYFlutter/flutter-article) + +|--|--|--|--| +|---|---|---|---| +| 基础控件 | 材料设计组件 | iOS风格控件 | Layout(布局)| +| 文本 | 资源 图片 图标 | Input (输入)| Animation and motion(动画和动效)| +| 交互模型 | 样式 | Painting and effect(绘制和效果)| Async(异步)| +| Scrolling (滑动) | Accessibility(辅助功能)| - | - | + ## 项目交流 diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb new file mode 100644 index 0000000..e69de29 From 667ef650f46886f03424b60184b397ddb22082e1 Mon Sep 17 00:00:00 2001 From: KenZR Date: Tue, 3 Sep 2019 18:27:29 +0800 Subject: [PATCH 098/100] =?UTF-8?q?refactor:=E4=BC=98=E5=8C=96=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E7=89=88=E6=9C=AC=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 6 +++-- lib/page/mine/index.dart | 46 +++++++++++++++++++-------------------- lib/utils/appVersion.dart | 6 +++-- pubspec.yaml | 2 +- 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 7286253..2d57adb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -71,7 +71,9 @@ class MainAppState extends State { } void main() => runApp( - Store.init( - child: MainApp(), + OKToast( + child: Store.init( + child: MainApp(), + ), ), ); diff --git a/lib/page/mine/index.dart b/lib/page/mine/index.dart index 5f5a59e..b7a192b 100644 --- a/lib/page/mine/index.dart +++ b/lib/page/mine/index.dart @@ -6,6 +6,7 @@ import 'package:efox_flutter/store/index.dart' show ConfigModel, Store; import 'package:efox_flutter/config/color.dart' show materialColor; import 'package:efox_flutter/utils/appVersion.dart' show AppVersion; import 'package:efox_flutter/components/expansion_tile.dart' as Comp; +import 'package:oktoast/oktoast.dart'; class _IndexState extends State { @override @@ -108,7 +109,7 @@ class _IndexState extends State { ) ], ), - /* Divider( + /* Divider( color: Color(AppTheme.lineColor), ), ListTile( @@ -123,28 +124,27 @@ class _IndexState extends State { Divider( color: Color(AppTheme.lineColor), ), - (Platform.isAndroid) - ? ListTile( - onTap: () { - AppVersion().check(context, showTips: true); - }, - leading: Icon(Icons.history), - title: Text(AppLocalizations.$t('common_mine_1.version')), - trailing: Container( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(Store.value(context).appVersion), - Icon(Icons.navigate_next) - ], - ), - )) - : Container(), - (Platform.isAndroid) - ? Divider( - color: Color(AppTheme.lineColor), - ) - : Container(), + if (Platform.isAndroid) + ListTile( + onTap: () { + AppVersion().check(context, showTips: true); + }, + leading: Icon(Icons.history), + title: Text(AppLocalizations.$t('common_mine_1.version')), + trailing: Container( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(Store.value(context).appVersion), + Icon(Icons.navigate_next) + ], + ), + ), + ), + if (Platform.isAndroid) + Divider( + color: Color(AppTheme.lineColor), + ), ], )); } diff --git a/lib/utils/appVersion.dart b/lib/utils/appVersion.dart index eeb865b..e5f56c7 100644 --- a/lib/utils/appVersion.dart +++ b/lib/utils/appVersion.dart @@ -7,6 +7,7 @@ import 'package:open_file/open_file.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:efox_flutter/http/index.dart' as Http; import 'package:flutter/material.dart'; +import 'package:oktoast/oktoast.dart'; class AppVersion { Future _checkPermission() async { @@ -45,9 +46,10 @@ class AppVersion { if (d['isNew']) { this._showDialog(context, d); } else if (showTips) { - Scaffold.of(context).showSnackBar(new SnackBar( + showToast('已经是最新版本'); + /*Scaffold.of(context).showSnackBar(new SnackBar( content: new Text('已经是最新版本'), - )); + ));*/ } } diff --git a/pubspec.yaml b/pubspec.yaml index 90e4af4..9a9a3f8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,7 +10,7 @@ description: A new Flutter project. version: 1.0.3+1 environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.2.2 <3.0.0" dependencies: flutter: From 0a9d9ad0978d28dc925d9d3fd0012315db4bcc97 Mon Sep 17 00:00:00 2001 From: KenZR Date: Fri, 6 Sep 2019 16:26:51 +0800 Subject: [PATCH 099/100] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E6=8E=A7=E5=88=B6=E9=97=AE=E9=A2=98=20fix:No=20Materi?= =?UTF-8?q?alLocalizations=20found=20=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 17 ++++++++++++----- lib/store/index.dart | 3 ++- pubspec.yaml | 2 +- version.json | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 2d57adb..e53f8a7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -70,10 +70,17 @@ class MainAppState extends State { } } -void main() => runApp( - OKToast( - child: Store.init( - child: MainApp(), - ), +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: OKToast( + child: Store.init(child: MainApp()), ), ); + } +} + +void main() => runApp( + MyApp(), + ); diff --git a/lib/store/index.dart b/lib/store/index.dart index 6f1130e..dd2d030 100644 --- a/lib/store/index.dart +++ b/lib/store/index.dart @@ -17,7 +17,8 @@ class Store { return context; } - static init({child}) { + static init({child,context}) { + Store.context ??= context; return MultiProvider( child: child, providers: [ diff --git a/pubspec.yaml b/pubspec.yaml index 9a9a3f8..6b5ffed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ description: A new Flutter project. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # Read more about versioning at semver.org. -version: 1.0.3+1 +version: 1.0.4+1 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/version.json b/version.json index bcfa2c4..630f85e 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"1.0.3"} +{"version":"1.0.4"} From e0919a867dfcec7d636bf3df5082c8bb670d3f26 Mon Sep 17 00:00:00 2001 From: KenZR Date: Mon, 9 Sep 2019 18:18:58 +0800 Subject: [PATCH 100/100] Update readme.md --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 9fa29f7..f1029ec 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# Flutter UI v1.0.3 +# Flutter UI v1.0.4 + [项目正筹划新一轮更新欢迎建议OR提ISSUE](https://github.com/YYFlutter/flutter-ui/issues/55) + [点击下载](https://github.com/YYFlutter/flutter-ui/releases/download/v1.0.3/app-release.apk)