diff --git a/assets/images/calendar.png b/assets/images/calendar.png new file mode 100644 index 00000000..505cfd85 Binary files /dev/null and b/assets/images/calendar.png differ diff --git a/assets/images/house.png b/assets/images/house.png new file mode 100644 index 00000000..cd2b9ba8 Binary files /dev/null and b/assets/images/house.png differ diff --git a/assets/images/p1.png b/assets/images/p1.png new file mode 100644 index 00000000..063ba531 Binary files /dev/null and b/assets/images/p1.png differ diff --git a/assets/images/p2.png b/assets/images/p2.png new file mode 100644 index 00000000..31ad2ed6 Binary files /dev/null and b/assets/images/p2.png differ diff --git a/assets/images/p3.png b/assets/images/p3.png new file mode 100644 index 00000000..2ee3cc03 Binary files /dev/null and b/assets/images/p3.png differ diff --git a/assets/images/plane.png b/assets/images/plane.png new file mode 100644 index 00000000..1ddb1ac5 Binary files /dev/null and b/assets/images/plane.png differ diff --git a/lib/common/example_code_parser.dart b/lib/common/example_code_parser.dart index fa7e5b33..a045816d 100644 --- a/lib/common/example_code_parser.dart +++ b/lib/common/example_code_parser.dart @@ -14,8 +14,6 @@ import 'package:flutter/services.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter/material.dart'; -const String _kStartTag = '// START '; -const String _kEndTag = '// END'; Map _exampleCode; String _code; @@ -38,7 +36,6 @@ Future _parseExampleCode(context,String filePath, AssetBundle bundle) asyn try { code = await bundle.loadString('lib/widgets/$filePath'); } catch (err) { - print('${Application.github['widgetsURL']} $filePath'); Navigator.of(context).pop(); _launchURL(Application.github['widgetsURL'] + filePath); } diff --git a/lib/common/list_view_item.dart b/lib/common/list_view_item.dart index cef7d7a4..7580cc0e 100644 --- a/lib/common/list_view_item.dart +++ b/lib/common/list_view_item.dart @@ -1,5 +1,14 @@ +/* + * @Author: 一凨 + * @Date: 2019-01-14 17:53:54 + * @Last Modified by: 一凨 + * @Last Modified time: 2019-01-14 17:57:51 + */ import 'package:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; +import '../routers/application.dart'; +import '../routers/routers.dart'; +import 'dart:core'; + class ListViewItem extends StatelessWidget { final String itemUrl; @@ -9,15 +18,6 @@ class ListViewItem extends StatelessWidget { const ListViewItem({Key key, this.itemUrl, this.itemTitle, this.data}) : super(key: key); - void _launchURL(String url, BuildContext context) async { - - if (await canLaunch(url)) { - await launch(url); - } else { - throw 'Could not launch $url'; - } - - } @override Widget build(BuildContext context) { @@ -27,7 +27,8 @@ class ListViewItem extends StatelessWidget { margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0), child: ListTile( onTap: () { - _launchURL(itemUrl, context); + // _launchURL(itemUrl, context); + Application.router.navigateTo(context, '${Routes.webViewPage}?title=${Uri.encodeComponent(itemTitle)}&url=${Uri.encodeComponent(itemUrl)}'); }, title: Padding( child: Text( diff --git a/lib/common/provider.dart b/lib/common/provider.dart index 32703214..6704fc9d 100644 --- a/lib/common/provider.dart +++ b/lib/common/provider.dart @@ -46,7 +46,6 @@ class Provider { for(int i = 0; i < expectTables.length; i++) { if (!tables.contains(expectTables[i])) { - print("table lost in app"); return false; } } diff --git a/lib/common/widget_demo.dart b/lib/common/widget_demo.dart index 6277c800..f64bde54 100644 --- a/lib/common/widget_demo.dart +++ b/lib/common/widget_demo.dart @@ -4,17 +4,14 @@ * 新widget详情页模板 */ import 'package:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; import '../routers/application.dart'; +import '../routers/routers.dart'; import '../components/markdown.dart'; import '../model/collection.dart'; import '../widgets/index.dart'; import 'package:fluttertoast/fluttertoast.dart'; import '../event/event-bus.dart'; import '../event/event-model.dart'; -import './full_screen_code_dialog.dart'; -import '../routers/application.dart'; -import '../routers/routers.dart'; import 'dart:core'; class WidgetDemo extends StatefulWidget { @@ -40,10 +37,9 @@ class WidgetDemo extends StatefulWidget { class _WidgetDemoState extends State { bool _hasCollected = false; CollectionControlModel _collectionControl = new CollectionControlModel(); - Color _collectionColor; + var _collectionIcons; List widgetDemosList = new WidgetDemoList().getDemos(); String _router = ''; - String _collText = ''; void showInSnackBar(String value) { Fluttertoast.showToast( @@ -55,13 +51,6 @@ class _WidgetDemoState extends State { textColor: Colors.white); } - void _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url); - } else { - throw 'Could not launch $url'; - } - } List _buildContent() { List _list = [ @@ -143,24 +132,20 @@ class _WidgetDemoState extends State { void _selectValue(value){ if(value == 'doc'){ - _launchURL(widget.docUrl); + // _launchURL(widget.docUrl); + Application.router.navigateTo(context, '${Routes.webViewPage}?title=${Uri.encodeComponent(widget.title)} Doc&&url=${Uri.encodeComponent(widget.docUrl)}'); }else if(value =='code'){ - // _launchURL(Application.github['widgetsURL'] + widget.codeUrl); Application.router.navigateTo(context, '${Routes.codeView}?filePath=${Uri.encodeComponent(widget.codeUrl)}'); - }else{ - _getCollection(); } } @override Widget build(BuildContext context) { if (_hasCollected) { - _collectionColor = Colors.red; - _collText='取消收藏'; + _collectionIcons = Icons.favorite; } else { - _collectionColor =null; - _collText='组件收藏'; + _collectionIcons = Icons.favorite_border; } return Scaffold( appBar: AppBar( @@ -173,6 +158,11 @@ class _WidgetDemoState extends State { }, icon: Icon(Icons.home), ), + new IconButton( + tooltip: 'collection', + onPressed: _getCollection, + icon: Icon(_collectionIcons), + ), PopupMenuButton( onSelected: _selectValue, itemBuilder: (BuildContext context) => >[ @@ -191,15 +181,6 @@ class _WidgetDemoState extends State { title: Text('查看Demo'), ), ), - const PopupMenuDivider(), - PopupMenuItem( - value: 'collection', - child: ListTile( - leading: Icon(Icons.star,size: 22.0,color: _collectionColor,), - title: Text(_collText), - - ), - ), ], ), ], diff --git a/lib/components/fourth_page_feature/page_dragger.dart b/lib/components/fourth_page_feature/page_dragger.dart new file mode 100644 index 00000000..5b00e29f --- /dev/null +++ b/lib/components/fourth_page_feature/page_dragger.dart @@ -0,0 +1,192 @@ + +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import './pager_indicator.dart'; + +class PageDragger extends StatefulWidget { + + final canDragLeftToRight; + final canDragRightToLeft; + + final StreamController slideUpdateStream; + + + PageDragger({ + this.canDragLeftToRight, + this.canDragRightToLeft, + this.slideUpdateStream, + }); + + @override + _PageDraggerState createState() => _PageDraggerState(); +} + +class _PageDraggerState extends State { + + static const FULL_TRANSTITION_PX = 300.0; + + Offset dragStart; + SlideDirection slideDirection; + double slidePercent = 0.0; + + onDragStart(DragStartDetails details){ + dragStart = details.globalPosition; + } + + onDragUpdate(DragUpdateDetails details) { + if (dragStart != null) { + final newPosition = details.globalPosition; + final dx = dragStart.dx - newPosition.dx; + + if (dx > 0 && widget.canDragRightToLeft) { + slideDirection = SlideDirection.rightToLeft; + } else if (dx < 0 && widget.canDragLeftToRight) { + slideDirection = SlideDirection.leftToRight; + } else { + slideDirection = SlideDirection.none; + } + + if (slideDirection != SlideDirection.none){ + slidePercent = (dx / FULL_TRANSTITION_PX).abs().clamp(0.0, 1.0); + } else { + slidePercent = 0.0; + } + widget.slideUpdateStream.add( + new SlideUpdate( + UpdateType.dragging, + slideDirection, + slidePercent + )); + } + } + + onDragEnd(DragEndDetails details){ + widget.slideUpdateStream.add( + new SlideUpdate( + UpdateType.doneDragging, + SlideDirection.none, + 0.0, + ) + ); + + dragStart = null; + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onHorizontalDragStart: onDragStart , + onHorizontalDragUpdate: onDragUpdate , + onHorizontalDragEnd: onDragEnd , + ); + } +} + +class AnimatedPageDragger{ + + static const PERCENT_PER_MILLISECOND = 0.005; + + final slideDirection; + final transitionGoal; + + AnimationController completionAnimationController; + + AnimatedPageDragger({ + this.slideDirection, + this.transitionGoal, + slidePercent, + StreamController slideUpdateStream, + TickerProvider vsync, + }) { + final startSlidePercent = slidePercent; + var endSlidePercent; + var duration; + + if ( transitionGoal == TransitionGoal.open){ + endSlidePercent = 1.0; + + final slideRemaining = 1.0 - slidePercent; + + duration = new Duration( + milliseconds: (slideRemaining / PERCENT_PER_MILLISECOND).round() + ); + + } else { + endSlidePercent = 0.0; + duration = new Duration( + milliseconds: (slidePercent / PERCENT_PER_MILLISECOND).round() + ); + } + + completionAnimationController = new AnimationController( + duration: duration, + vsync: vsync + ) + ..addListener((){ + slidePercent = lerpDouble( + startSlidePercent, + endSlidePercent, + completionAnimationController.value + ); + + slideUpdateStream.add( + new SlideUpdate( + UpdateType.animating, + slideDirection, + slidePercent, + ) + ); + + }) + + ..addStatusListener((AnimationStatus status){ + + if(status == AnimationStatus.completed){ + slideUpdateStream.add( + new SlideUpdate( + UpdateType.doneAnimating, + slideDirection, + endSlidePercent, + ) + ); + } + + }); + + } + + run(){ + completionAnimationController.forward(from: 0.0); + } + + dispose(){ + completionAnimationController.dispose(); + } + +} + +enum TransitionGoal{ + open, + close, +} + +enum UpdateType{ + dragging, + doneDragging, + animating, + doneAnimating, +} + +class SlideUpdate { + final updateType; + final direction; + final slidePercent; + + SlideUpdate( + this.updateType, + this.direction, + this.slidePercent + ); +} diff --git a/lib/components/fourth_page_feature/page_reveal.dart b/lib/components/fourth_page_feature/page_reveal.dart new file mode 100644 index 00000000..2cc1490c --- /dev/null +++ b/lib/components/fourth_page_feature/page_reveal.dart @@ -0,0 +1,53 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +class PageReveal extends StatelessWidget { + + final double revealPercent; + final Widget child; + + PageReveal({ + this.revealPercent, + this.child + }); + + @override + Widget build(BuildContext context) { + return ClipOval( + clipper: new CircleRevealClipper(revealPercent), + child: child, + ); + } +} + +class CircleRevealClipper extends CustomClipper{ + + final double revealPercent; + + + CircleRevealClipper( + this.revealPercent + ); + + @override + Rect getClip(Size size) { + + final epicenter = new Offset(size.width / 2, size.height * 0.9); + + double theta = atan(epicenter.dy / epicenter.dx); + final distanceToCorner = epicenter.dy / sin(theta); + + final radius = distanceToCorner * revealPercent; + final diameter = 2 * radius; + + return new Rect.fromLTWH(epicenter.dx - radius, epicenter.dy - radius, diameter, diameter); + } + + @override + bool shouldReclip(CustomClipper oldClipper) { + // TODO: implement shouldReclip + return true; + } + +} \ No newline at end of file diff --git a/lib/components/fourth_page_feature/pager_indicator.dart b/lib/components/fourth_page_feature/pager_indicator.dart new file mode 100644 index 00000000..1889165c --- /dev/null +++ b/lib/components/fourth_page_feature/pager_indicator.dart @@ -0,0 +1,156 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import './pages.dart'; + +class PagerIndicator extends StatelessWidget { + + final PagerIndicatorViewModel viewModel; + + PagerIndicator({ + this.viewModel, + }); + + @override + Widget build(BuildContext context) { + + List bubbles = []; + for(var i = 0; i < viewModel.pages.length; ++i ){ + final page = viewModel.pages[i]; + + var percentActive; + + if(i == viewModel.activeIndex){ + percentActive = 1.0 - viewModel.slidePercent; + } else if (i == viewModel.activeIndex - 1 && viewModel.slideDirection == SlideDirection.leftToRight){ + percentActive = viewModel.slidePercent; + } else if (i == viewModel.activeIndex + 1 && viewModel.slideDirection == SlideDirection.rightToLeft){ + percentActive = viewModel.slidePercent; + }else { + percentActive = 0.0; + } + + bool isHollow = i > viewModel.activeIndex || (i == viewModel.activeIndex && viewModel.slideDirection == SlideDirection.leftToRight); + + + + bubbles.add( + new PageBubble( + viewModel: new PageBubbleViewModel( + page.iconAssetPath, + page.color, + isHollow, + percentActive, + ), + ), + ); + } + + final BUBBLE_WIDHT = 55.0 ; + final baseTranslation = ((viewModel.pages.length * BUBBLE_WIDHT) / 2) - (BUBBLE_WIDHT / 2) ; + var translation = baseTranslation - (viewModel.activeIndex * BUBBLE_WIDHT); + + if (viewModel.slideDirection == SlideDirection.leftToRight){ + translation += BUBBLE_WIDHT * viewModel.slidePercent; + }else if (viewModel.slideDirection == SlideDirection.rightToLeft){ + translation -= BUBBLE_WIDHT * viewModel.slidePercent; + } + + return new Column( + children: [ + new Expanded(child: new Container()), + new Transform( + transform: new Matrix4.translationValues(translation, 0.0, 0.0), + child: new Row( + mainAxisAlignment: MainAxisAlignment.center, + children: bubbles, + ), + ), + ], + ); + } +} + +enum SlideDirection{ + leftToRight, + rightToLeft, + none, +} + + +class PagerIndicatorViewModel{ + final List pages; + final int activeIndex; + final SlideDirection slideDirection; + final double slidePercent; + + PagerIndicatorViewModel( + this.pages, + this.activeIndex, + this.slideDirection, + this.slidePercent + ); + + +} + +class PageBubble extends StatelessWidget { + + final PageBubbleViewModel viewModel; + + + PageBubble({ + this.viewModel + }); + + @override + Widget build(BuildContext context) { + return new Container( + width: 55.0, + height: 65.0, + child: new Center( + child: new Container( + width: lerpDouble(20.0,45.0,viewModel.activePercent), + height: lerpDouble(20.0,45.0,viewModel.activePercent), + decoration: new BoxDecoration( + shape: BoxShape.circle, + color: viewModel.isHollow + ? const Color(0x88FFFFFF).withAlpha(0x88 * viewModel.activePercent.round()) + : const Color(0x88FFFFFF), + border: new Border.all( + color: viewModel.isHollow + ? const Color(0x88FFFFFF).withAlpha((0x88 * (1.0 - viewModel.activePercent)).round()) + : Colors.transparent, + width: 3.0, + ), + ), + child: new Opacity( + opacity: viewModel.activePercent, + child: Image.asset( + viewModel.iconAssetPath, + color: viewModel.color, + ), + ), + ), + ), + ); + } +} + + +class PageBubbleViewModel { + final String iconAssetPath; + final Color color; + final bool isHollow; + final double activePercent; + + PageBubbleViewModel ( + this.iconAssetPath, + this.color, + this.isHollow, + this.activePercent, + ); + + +} + diff --git a/lib/components/fourth_page_feature/pages.dart b/lib/components/fourth_page_feature/pages.dart new file mode 100644 index 00000000..e21ea01e --- /dev/null +++ b/lib/components/fourth_page_feature/pages.dart @@ -0,0 +1,136 @@ +import 'package:flutter/material.dart'; +import '../../routers/application.dart'; +import '../../routers/routers.dart'; + +final pages = [ + new PageViewModel( + const Color(0xFFcd344f), + //'assets/mountain.png', + 'assets/images/p2.png', + 'FlutterGo是什么?', + '【FlutterGo】 是由"阿里拍卖"前端团队几位 Flutter 粉丝,用业余时间开发的一款,用于 Flutter 教学帮助的App,这里没有高大尚的概念,只有一个一个亲历的尝试,用最直观的方式展示的 Flutter 官方demo', + 'assets/images/plane.png'), + new PageViewModel( + const Color(0xFF638de3), + //'assets/world.png', + 'assets/images/p1.png', + 'FLutterGo的背景', + '🐢 官网文档示例较不够健全,不够直观\n🐞 运行widget demo要到处翻阅资料\n🐌 英文文档翻译生涩难懂,学习资料太少\n', + 'assets/images/calendar.png'), + new PageViewModel( + const Color(0xFFFF682D), + //'assets/home.png', + 'assets/images/p3.png', + 'FlutterGo的特点', + '🐡 详解常用widget多达 130+ 个\n🦋 持续迭代追新官方版本\n🐙 配套Demo详解widget用法\n🚀 一站式搞定所有常用widget,开箱即查\n', + 'assets/images/house.png', + ), +]; + +class Page extends StatelessWidget { + final PageViewModel viewModel; + final double percentVisible; + + Page({ + this.viewModel, + this.percentVisible = 1.0, + }); + + Widget creatButton(BuildContext context,String txt,IconData iconName,String type){ + return RaisedButton.icon( + onPressed: () { + if(type == 'start'){ + //Navigator.popUntil(context, ModalRoute.withName('/')); + }else if(type == 'goGithub'){ + Application.router.navigateTo(context, '${Routes.webViewPage}?title=${Uri.encodeComponent(txt)} Doc&&url=${Uri.encodeComponent("https://github.com/alibaba/flutter-go")}'); + } + }, + elevation: 10.0, + color:Colors.black26, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20)), + //如果不手动设置icon和text颜色,则默认使用foregroundColor颜色 + icon: new Icon(iconName,color: Colors.white,size:20.0), + label: new Text(txt, maxLines: 1,style: TextStyle(color:Colors.white,fontSize: 16,fontWeight: FontWeight.w700),) + ); + } + + @override + Widget build(BuildContext context) { + return new Container( + width: double.infinity, + color: viewModel.color, + child: new Opacity( + opacity: percentVisible, + child: new Column( + crossAxisAlignment:CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + + new Transform( + transform: new Matrix4.translationValues(0.0, 50.0 * (1.0 - percentVisible) ,0.0), + child: new Padding( + padding: new EdgeInsets.only(top: 20.0, bottom: 10.0), + child: + new Image.asset( + viewModel.heroAssetPath, + width: 200.0, + height: 200.0), + ), + ), + new Transform( + transform: new Matrix4.translationValues(0.0, 30.0 * (1.0 - percentVisible) ,0.0), + child: new Padding( + padding: new EdgeInsets.only(top: 10.0, bottom: 10.0), + child: new Text( + viewModel.title, + style: new TextStyle( + color: Colors.white, + fontFamily: 'FlamanteRoma', + fontSize: 34.0, + ), + ), + ), + ), + new Transform( + transform: new Matrix4.translationValues(0.0, 30.0 * (1.0 - percentVisible) ,0.0), + child: new Padding( + padding: new EdgeInsets.only(bottom: 10.0), + child: new Text( + viewModel.body, + textAlign: TextAlign.center, + style: new TextStyle( + height:1.2, + color: Colors.white, + fontFamily: 'FlamanteRomaItalic', + fontSize: 18.0, + ), + ), + ), + ), + ButtonBar( + alignment: MainAxisAlignment.center, + children: [ + creatButton(context,'开始使用',Icons.add_circle_outline,'start'), + creatButton(context,'GitHub',Icons.arrow_forward,'goGithub'), + ],) + ]), + )); + } +} + +class PageViewModel { + final Color color; + final String heroAssetPath; + final String title; + final String body; + final String iconAssetPath; + + PageViewModel( + this.color, + this.heroAssetPath, + this.title, + this.body, + this.iconAssetPath, + ); +} diff --git a/lib/main.dart b/lib/main.dart index e567cfb1..2b14dc77 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -168,8 +168,8 @@ class _MyHomePageState extends State tabs: [ Tab(text: '业界动态', icon: Icon(Icons.language)), Tab(text: '组件', icon: Icon(Icons.extension)), - Tab(text: '组件收藏', icon: Icon(Icons.star)), - Tab(text: '关于手册', icon: Icon(Icons.favorite)), + Tab(text: '组件收藏', icon: Icon(Icons.favorite)), + Tab(text: '关于手册', icon: Icon(Icons.line_weight)), ], ), ), diff --git a/lib/routers/router_handler.dart b/lib/routers/router_handler.dart index f852e4a9..cd16e709 100644 --- a/lib/routers/router_handler.dart +++ b/lib/routers/router_handler.dart @@ -3,24 +3,32 @@ import 'package:fluro/fluro.dart'; import '../views/category.dart'; import '../widgets/404.dart'; import '../common/full_screen_code_dialog.dart'; +import '../views/web_view_page.dart'; var categoryHandler = new Handler( - handlerFunc: (BuildContext context, Map> params) { - String name = params["type"]?.first; + handlerFunc: (BuildContext context, Map> params) { + String name = params["type"]?.first; - return new CategoryHome(name); - }, - ); + return new CategoryHome(name); + }, +); var widgetNotFoundHandler = new Handler( - handlerFunc: (BuildContext context, Map> params) { - return new WidgetNotFound(); - } -); + handlerFunc: (BuildContext context, Map> params) { + return new WidgetNotFound(); +}); var fullScreenCodeDialog = new Handler( - handlerFunc: (BuildContext context,Map> params){ - String path = params['filePath']?.first; - return new FullScreenCodeDialog(filePath: path,); - } -); + handlerFunc: (BuildContext context, Map> params) { + String path = params['filePath']?.first; + return new FullScreenCodeDialog( + filePath: path, + ); +}); + +var webViewPageHand = new Handler( + handlerFunc: (BuildContext context, Map> params) { + String title = params['title']?.first; + String url = params['url']?.first; + return new WebViewPage(url, title); +}); diff --git a/lib/routers/routers.dart b/lib/routers/routers.dart index 03310fef..833b442e 100644 --- a/lib/routers/routers.dart +++ b/lib/routers/routers.dart @@ -8,6 +8,7 @@ class Routes { static String root = "/"; static String widgetDemo = '/widget-demo'; static String codeView = '/code-view'; + static String webViewPage = '/web-view-page'; static void configureRoutes(Router router) { List widgetDemosList = new WidgetDemoList().getDemos(); @@ -19,6 +20,7 @@ class Routes { router.define('/category/:type', handler: categoryHandler); router.define('/category/error/404', handler: widgetNotFoundHandler); router.define(codeView,handler:fullScreenCodeDialog); + router.define(webViewPage,handler:webViewPageHand); widgetDemosList.forEach((demo) { Handler handler = new Handler( handlerFunc: (BuildContext context, Map> params) { diff --git a/lib/views/collection_page.dart b/lib/views/collection_page.dart index 6e99f80e..1629b67e 100644 --- a/lib/views/collection_page.dart +++ b/lib/views/collection_page.dart @@ -2,11 +2,12 @@ * @Author: 一凨 * @Date: 2019-01-08 17:12:58 * @Last Modified by: 一凨 - * @Last Modified time: 2019-01-08 20:14:56 + * @Last Modified time: 2019-01-14 20:02:46 */ import 'package:flutter/material.dart'; import '../model/collection.dart'; import '../routers/application.dart'; +import '../routers/routers.dart'; import '../event/event-bus.dart'; import '../event/event-model.dart'; import 'package:event_bus/event_bus.dart'; @@ -15,8 +16,7 @@ class CollectionPage extends StatefulWidget { _CollectionPageState createState() => _CollectionPageState(); } -class _CollectionPageState extends State - with AutomaticKeepAliveClientMixin { +class _CollectionPageState extends State { _CollectionPageState() { final eventBus = new EventBus(); ApplicationEvent.event = eventBus; @@ -24,9 +24,7 @@ class _CollectionPageState extends State CollectionControlModel _collectionControl = new CollectionControlModel(); List _collectionList = []; ScrollController _scrollController = new ScrollController(); - - @override - bool get wantKeepAlive => true; + var _icons; @override void initState() { @@ -76,8 +74,17 @@ class _CollectionPageState extends State ), ); } + if (_collectionList[index - 1].router.contains('http')) { + if (_collectionList[index - 1].name.endsWith('Doc')) { + _icons = Icons.library_books; + } else { + _icons = Icons.bookmark; + } + } else { + _icons = Icons.extension; + } return Container( - padding: const EdgeInsets.all(10.0), + padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 5.0), margin: const EdgeInsets.only(bottom: 7.0), decoration: BoxDecoration( color: Colors.white, @@ -91,15 +98,26 @@ class _CollectionPageState extends State ], ), child: ListTile( + leading: Icon( + _icons, + size: 30.0, + color: Theme.of(context).primaryColor, + ), title: Text( _collectionList[index - 1].name, + overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 17.0), ), trailing: Icon(Icons.keyboard_arrow_right, color: Colors.grey, size: 30.0), onTap: () { - Application.router - .navigateTo(context, "${_collectionList[index - 1].router}"); + if (_collectionList[index - 1].router.contains('http')) { + Application.router.navigateTo(context, + '${Routes.webViewPage}?title=${Uri.encodeComponent(_collectionList[index - 1].name)}&url=${Uri.encodeComponent(_collectionList[index - 1].router)}'); + } else { + Application.router + .navigateTo(context, "${_collectionList[index - 1].router}"); + } }, ), ); @@ -107,7 +125,6 @@ class _CollectionPageState extends State @override Widget build(BuildContext context) { - super.build(context); if (_collectionList.length == 0) { return ListView( children: [ diff --git a/lib/views/fourth_page.dart b/lib/views/fourth_page.dart index fc48f1db..0aa96e35 100644 --- a/lib/views/fourth_page.dart +++ b/lib/views/fourth_page.dart @@ -1,22 +1,118 @@ +import 'dart:async'; import 'package:flutter/material.dart'; -import '../components/comp_list.dart'; +import 'package:flutter_go/components/fourth_page_feature/page_dragger.dart'; +import 'package:flutter_go/components/fourth_page_feature/page_reveal.dart'; +import 'package:flutter_go/components/fourth_page_feature/pager_indicator.dart'; +import 'package:flutter_go/components/fourth_page_feature/pages.dart'; +import '../components/comp_list.dart'; class FourthPage extends StatefulWidget { @override FourthPageState createState() => new FourthPageState(); } -class FourthPageState extends State { +class FourthPageState extends State with TickerProviderStateMixin { + + StreamController slideUpdateStream; + AnimatedPageDragger animatedPageDragger; + + int activeIndex = 0 ; + SlideDirection slideDirection = SlideDirection.none; + int nextPageIndex = 0 ; + double slidePercent= 0.0; + + FourthPageState(){ + slideUpdateStream = new StreamController(); + + slideUpdateStream.stream.listen((SlideUpdate event){ + setState(() { + if( event.updateType == UpdateType.dragging){ + slideDirection = event.direction; + slidePercent = event.slidePercent; + + if( slideDirection == SlideDirection.leftToRight ){ + nextPageIndex = activeIndex - 1; + } else if (slideDirection == SlideDirection.rightToLeft){ + nextPageIndex = activeIndex + 1; + } else{ + nextPageIndex = activeIndex; + } + } else if( event.updateType == UpdateType.doneDragging){ + if(slidePercent > 0.5){ + + animatedPageDragger = new AnimatedPageDragger( + slideDirection: slideDirection, + transitionGoal: TransitionGoal.open, + slidePercent: slidePercent, + slideUpdateStream: slideUpdateStream, + vsync: this, + ); + + } else{ + animatedPageDragger = new AnimatedPageDragger( + slideDirection: slideDirection, + transitionGoal: TransitionGoal.close, + slidePercent: slidePercent, + slideUpdateStream: slideUpdateStream, + vsync: this, + ); + + nextPageIndex = activeIndex; + } + + animatedPageDragger.run(); + } + else if( event.updateType == UpdateType.animating){ + slideDirection = event.direction; + slidePercent = event.slidePercent; + } + + else if (event.updateType == UpdateType.doneAnimating){ + activeIndex = nextPageIndex; + + slideDirection = SlideDirection.none; + slidePercent = 0.0; + + animatedPageDragger.dispose(); + } + }); + }); + + + } @override Widget build(BuildContext context) { - return new Container( - child: new CompList() + return new Stack( + children: [ + new Page( // page 的主要内容 + viewModel: pages[activeIndex], + percentVisible: 1.0 , + ), + new PageReveal( + revealPercent: slidePercent, + child: new Page( + viewModel: pages[nextPageIndex], + percentVisible: slidePercent , + ), + ), + new PagerIndicator( + viewModel: new PagerIndicatorViewModel( + pages, + activeIndex, + slideDirection, + slidePercent, + ), + ), + new PageDragger( + canDragLeftToRight: activeIndex > 0 , + canDragRightToLeft: activeIndex < pages.length - 1 , + slideUpdateStream: this.slideUpdateStream, + ) + ], ); } } - - diff --git a/lib/views/web_view_page.dart b/lib/views/web_view_page.dart new file mode 100644 index 00000000..27b0eb78 --- /dev/null +++ b/lib/views/web_view_page.dart @@ -0,0 +1,120 @@ +/* + * @Author: 一凨 + * @Date: 2019-01-14 17:44:47 + * @Last Modified by: 一凨 + * @Last Modified time: 2019-01-14 19:47:14 + */ +import 'package:flutter/material.dart'; +import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; +import '../model/collection.dart'; +import '../event/event-bus.dart'; +import '../event/event-model.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class WebViewPage extends StatefulWidget { + final String url; + final String title; + + WebViewPage(this.url, this.title); + _WebViewPageState createState() => _WebViewPageState(); +} + +class _WebViewPageState extends State { + bool _hasCollected = false; + String _router = ''; + var _collectionIcons; + CollectionControlModel _collectionControl = new CollectionControlModel(); + + void showInSnackBar(String value) { + Fluttertoast.showToast( + msg: value, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.CENTER, + timeInSecForIos: 1, + backgroundColor: Colors.grey, + textColor: Colors.white); + } + + @override + void initState() { + super.initState(); + _collectionControl.getRouterByName(widget.title.trim()).then((list) { + + list.forEach((item) { + if(widget.title.trim() == item['name']){ + _router = item['router']; + } + }); + if (mounted) { + setState(() { + _hasCollected = list.length > 0; + }); + } + }); + } + + // 点击收藏按钮 + _getCollection() { + if (_hasCollected) { + // 删除操作 + _collectionControl.deleteByName(widget.title.trim()).then((result) { + if (result > 0 && this.mounted) { + setState(() { + _hasCollected = false; + }); + showInSnackBar('已取消收藏'); + + if (ApplicationEvent.event != null) { + ApplicationEvent.event + .fire(CollectionEvent(widget.title, _router, true)); + } + return; + } + print('删除错误'); + }); + } else { + // 插入操作 + _collectionControl + .insert(Collection(name: widget.title.trim(), router: widget.url)) + .then((result) { + if (this.mounted) { + setState(() { + _hasCollected = true; + }); + + if (ApplicationEvent.event != null) { + ApplicationEvent.event + .fire(CollectionEvent(widget.title, _router, false)); + } + + showInSnackBar('收藏成功'); + } + }); + } + } + + @override + Widget build(BuildContext context) { + if (_hasCollected) { + _collectionIcons = Icons.favorite; + } else { + _collectionIcons = Icons.favorite_border; + } + return WebviewScaffold( + url: widget.url, + appBar: AppBar( + title: Text(widget.title), + actions: [ + new IconButton( + tooltip: 'goBack home', + onPressed: _getCollection, + icon: Icon(_collectionIcons,), + ), + ], + ), + withZoom: false, + withLocalStorage: true, + withJavascript: true, + ); + } +} diff --git a/lib/widgets/elements/Form/Button/PopupMenuButton/demo.dart b/lib/widgets/elements/Form/Button/PopupMenuButton/demo.dart index beb27696..40e8099f 100644 --- a/lib/widgets/elements/Form/Button/PopupMenuButton/demo.dart +++ b/lib/widgets/elements/Form/Button/PopupMenuButton/demo.dart @@ -96,7 +96,6 @@ class PopupMenuButtonCustom extends StatelessWidget { : super(); @override Widget build(BuildContext context) { - print('onSelected1:${widget.options}'); final String selectStr = widget.options['defaultSelect']; return PopupMenuButton( //如果提供,则用于此按钮的widget。 diff --git a/lib/widgets/elements/Form/Input/TextField/text_field_demo.dart b/lib/widgets/elements/Form/Input/TextField/text_field_demo.dart index 3a4cfc95..3d36b522 100644 --- a/lib/widgets/elements/Form/Input/TextField/text_field_demo.dart +++ b/lib/widgets/elements/Form/Input/TextField/text_field_demo.dart @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; -/* -* 基本示例 -* -*/ +// 基本示例 class DefaultTextField extends StatelessWidget { @override Widget build(BuildContext context) { diff --git a/lib/widgets/elements/Media/Image/MemoryImage/memory_image_demo.dart b/lib/widgets/elements/Media/Image/MemoryImage/memory_image_demo.dart index edb914a3..a7ad8132 100644 --- a/lib/widgets/elements/Media/Image/MemoryImage/memory_image_demo.dart +++ b/lib/widgets/elements/Media/Image/MemoryImage/memory_image_demo.dart @@ -20,7 +20,6 @@ class _MemoryImageDemoState extends State { super.initState(); rootBundle.load('assets/images/food01.jpeg').then((data) { if (mounted) { - print(data); setState(() { bytes = data.buffer.asUint8List(); }); diff --git a/pubspec.yaml b/pubspec.yaml index 5165f5ef..84cbc7ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: # 本地存储、收藏功能 shared_preferences: ^0.4.3 dio: ^1.0.6 + flutter_webview_plugin: ^0.3.0+2 dev_dependencies: flutter_test: