目前增長了路由跳轉,能夠帶參數跳轉頁面。下拉能夠自定義刷新樣式,IOS點擊Status Bar
回到頂部,目前已經測試過。狀態管理器使用Mobx
,我本身以爲對於Redux
使用起來會複雜一點,下面是提供的預覽GIF圖,卡頓現象是由於屏幕錄製的幀率有點低。git
項目地址:https://github.com/Tecode/flutter_book,不定時的更新,歡迎start。github
environment: sdk: ">=2.1.0 <3.0.0" dependencies: flutter: sdk: flutter mobx: flutter_mobx: // Mobx cupertino_icons: ^0.1.2 flutter_svg: ">=0.12.4" // 處理SVG圖片 carousel_slider: ^1.3.0 // 輪播圖 fluro: "^1.4.0" // 路由 provider: ^2.0.1 // 用於包裹mobx dev_dependencies: flutter_test: sdk: flutter build_runner: ^1.3.1 //Mobx依賴 mobx_codegen: // Mobx依賴
Flutter
版本Flutter 1.5.9-pre.223 • channel master • https://github.com/flutter/flutter.git Framework • revision b76a1e8312 (25 hours ago) • 2019-05-13 09:06:30 +0100 Engine • revision 816d3fc586 Tools • Dart 2.3.1 (build 2.3.1-dev.0.0 a0290f823c)
import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_book/containers/Entrance.dart'; import 'package:flutter_book/helpers/constants.dart' show AppColors; import 'package:flutter/services.dart'; void main() { // 修改系統狀態欄顏色 SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( systemNavigationBarColor: Color(AppColors.themeColor), // navigation bar color statusBarColor: Color(AppColors.themeColor), // status bar color )); runApp(MyApp());wenti }
appBar: AppBar( ... leading: IconButton( alignment: Alignment.centerRight, icon: SvgPicture.asset( 'assets/icon/icon_trophy.svg', width: Constants.appBarIconSize + 5.0, height: Constants.appBarIconSize + 5.0, ), onPressed: () { print("ok"); }, ) ... )
MediaQuery.of(context)
assets: - assets/icon/ - lib/containers/ - lib/model/ - lib/helpers/ - lib/routers/ - assets/images/
這裏我使用的是fluro
配置路由,這裏我偷一下懶了,就沒有使用原生的方法,不過他幫咱們封裝了好多的方法咱們能夠很方便的去使用它,下面說一下路由的配置。app
lib\routers\routers.dart
配置路由對應的模塊,能夠理解成Vue-router
或React-router
同樣,先要將對應的路由配置到你要跳轉的模塊去。less
import 'package:fluro/fluro.dart'; import 'package:flutter/material.dart'; import 'package:flutter_book/routers/route_handlers.dart'; class Routes { static String root = "/"; static String setting = "/setting"; static String detail = "/detail"; static String demoSimpleFixedTrans = "/demo/fixedtrans"; static String demoFunc = "/demo/func"; static String deepLink = "/message"; static void configureRoutes(Router router) { router.notFoundHandler = Handler( handlerFunc: (BuildContext context, Map<String, List<String>> params) { print("ROUTE WAS NOT FOUND !!!"); }); router.define(root, handler: rootHandler); router.define(setting, handler: settingRouteHandler); router.define(detail, handler: detailRouterHandler); } }
lib\routers\route_handlers.dart
在這裏能夠處理一些傳過來的參數,而後咱們將參數放入類中實例化。ide
import 'package:flutter_book/containers/Setting.dart'; import 'package:flutter_book/containers/FirstScreen.dart'; import 'package:flutter_book/containers/Detail.dart'; import 'package:fluro/fluro.dart'; import 'package:flutter/material.dart'; import 'package:flutter_book/helpers/fluro_convert_util.dart'; Handler rootHandler = Handler( handlerFunc: (BuildContext context, Map<String, List<String>> params) { return FirstScreen(); }); Handler settingRouteHandler = Handler( handlerFunc: (BuildContext context, Map<String, List<String>> params) { return Setting(); }); Handler detailRouterHandler = Handler( handlerFunc: (BuildContext context, Map<String, List<String>> params) { return Detail( title: FluroConvertUtils.fluroCnParamsDecode(params["title"]?.first)); });
lib\main.dart
將路由與Flutter
綁定,這樣你的路由就能夠生效了svg
class MyApp extends StatelessWidget { MyApp() { final router = new Router(); Routes.configureRoutes(router); Application.router = router; } // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Book', theme: ThemeData( primaryColor: Color(AppColors.themeColor), accentColor: Color(AppColors.themeColor), scaffoldBackgroundColor: Color(AppColors.themeColor)), home: Entrance(), onGenerateRoute: Application.router.generator, ); } }
import 'package:fluro/fluro.dart'; import 'package:flutter_book/routers/application.dart'; import 'package:flutter_book/helpers/fluro_convert_util.dart'; ...代碼省略了 Application.router.navigateTo( context, "/detail?title=${FluroConvertUtils.fluroCnParamsEncode('熱門圖書')}", transition: TransitionType.native );
import 'dart:convert'; /// fluro 參數編碼解碼工具類 class FluroConvertUtils { /// fluro 傳遞中文參數前,先轉換,fluro 不支持中文傳遞 static String fluroCnParamsEncode(String originalCn) { StringBuffer sb = StringBuffer(); var encoded = Utf8Encoder().convert(originalCn); encoded.forEach((val) => sb.write('$val,')); return sb.toString().substring(0, sb.length - 1).toString(); } /// fluro 傳遞後取出參數,解析 static String fluroCnParamsDecode(String encodedCn) { var decoded = encodedCn.split('[').last.split(']').first.split(','); var list = <int>[]; decoded.forEach((s) => list.add(int.parse(s.trim()))); return Utf8Decoder().convert(list); } }
import 'package:flutter_book/helpers/fluro_convert_util.dart'; Application.router.navigateTo( context, "/detail?title=${FluroConvertUtils.fluroCnParamsEncode('熱門圖書')}", transition: TransitionType.native, // transitionDuration: const Duration(milliseconds: 300), );
import 'package:flutter_book/helpers/fluro_convert_util.dart'; Handler detailRouterHandler = Handler( handlerFunc: (BuildContext context, Map<String, List<String>> params) { return Detail( title: FluroConvertUtils.fluroCnParamsDecode(params["title"]?.first)); });
environment: sdk: ">=2.1.0 <3.0.0" dependencies: flutter: sdk: flutter mobx: flutter_mobx: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 flutter_svg: ">=0.12.4" carousel_slider: ^1.3.0 fluro: "^1.4.0" provider: ^2.0.1 dev_dependencies: flutter_test: sdk: flutter build_runner: ^1.3.1 mobx_codegen:
這裏要使用到provider: ^2.0.1
,相似React
的Provider
。使用Provider
來包裹咱們的組件,使Mobx
和咱們的React
聯繫起來。工具
React Provider
<Provider {...store}> <Router history={browserHistory} <App /> </Router> </Provider>
Dart Provider
Dart Provider
也是同樣的道理,將Mobx
和Flutter
聯繫起來,lib/main.dart
完整代碼,這樣使用能夠保證你實例化的的store
是同一個類。測試
runApp(MultiProvider( providers: [ Provider<FindStore>( builder: (_) => FindStore(), ) ], child: MyApp(), ));
個人導航發現那一欄和下面的內容是分開的,當我點擊導航的切換按鈕就會改變顯示的頁面,這樣咱們能夠複用顯示層的UI
組件,數據放專門的文件去管理。ui
經過點擊而後改變數據findStore.setTile('tile', true);
this
lib/widgets/NavBar/FindNavBar.dart
import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_book/helpers/constants.dart'; import 'package:flutter_book/stores/findStore.dart'; import 'package:provider/provider.dart'; class FindNavBar extends StatelessWidget { @override Widget build(BuildContext context) { // 咱們的store final findStore = Provider.of<FindStore>(context); return Observer( builder: (_) => AppBar( title: Text("發現"), actions: <Widget>[ IconButton( alignment: Alignment.centerRight, onPressed: () { findStore.setTile('tile', true); findStore.counter(); }, icon: SvgPicture.asset( 'assets/icon/icon_more.svg', width: Constants.appBarIconSize + 2.0, height: Constants.appBarIconSize + 2.0, color: Color(findStore.tile ? AppColors.fontColor : AppColors.fontColorGray), ), ), IconButton( alignment: Alignment.centerLeft, onPressed: () { findStore.setTile('tile', false); }, icon: SvgPicture.asset( 'assets/icon/icon_cube.svg', width: Constants.appBarIconSize + 2.0, height: Constants.appBarIconSize + 2.0, color: Color(findStore.tile ? AppColors.fontColorGray : AppColors.fontColor), ), ), ], centerTitle: true, elevation: 0, ), ); } }
lib/containers/Find.dart
檢測到數據發生變化,頁面從新渲染獲得新的頁面
import 'package:flutter/material.dart'; import 'package:flutter_book/widgets/Find/BookTile.dart'; import 'package:flutter_book/widgets/Find/BookCover.dart'; import 'package:flutter_book/stores/findStore.dart'; import 'package:provider/provider.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; class Find extends StatefulWidget { @override _FindState createState() => _FindState(); } class _FindState extends State<Find> { @override Widget build(BuildContext context) { final findStore = Provider.of<FindStore>(context); return Observer(builder: (_) => findStore.tile ? BookTile() : BookCover()); } }
FindStore
lib/stores/findStore.dart
import 'package:mobx/mobx.dart'; // Include generated file part 'findStore.g.dart'; // This is the class used by rest of your codebase class FindStore = _FindStore with _$FindStore; // The store-class abstract class _FindStore implements Store { @observable bool tile = false; @observable num count = 0; @action void setTile(String key, dynamic value) => tile = value; @action num counter() => this.count++; }
若是你是不少個頁面共享一個Store
不要直接導入而後實例化,例如:
demo1.dart
這個頁面咱們導入了counter.dart
這個store
並且咱們將它實例化,當咱們點擊的時候數據發生變化頁面會從新渲染
import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'counter.dart'; // Import the Counter final counter = Counter(); // Instantiate the store void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'MobX', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), ); } } class MyHomePage extends StatelessWidget { const MyHomePage(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('MobX Counter'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( '數值是:', ), // Wrapping in the Observer will automatically re-render on changes to counter.value Observer( builder: (_) => Text( '${counter.value}', style: Theme.of(context).textTheme.display1, ), ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: counter.increment, tooltip: 'Increment', child: Icon(Icons.add), ), ); } }
demo2.dart
這個頁面咱們也導入了counter.dart
,咱們要的結果是第一個頁面的數據變化了也影響這個頁面,可是顯然是不能的。由於store
雖然是一個,可是實例化的時候是兩個不一樣的,因此第一個頁面的數據變化了也不會影響到這裏。
怎麼解決呢?咱們可使用以前提到的Provider
去將Mobx
與Flutter
聯繫起來而後經過上下關係去的到咱們想要的Store
,例如final findStore = Provider.of<FindStore>(context);
import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'counter.dart'; // Import the Counter final counter = Counter(); // Instantiate the store void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'MobX', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), ); } } class MyHomePage extends StatelessWidget { const MyHomePage(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('MobX Counter'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( '第二個頁面顯示第一個頁面的數是:', ), Observer( builder: (_) => Text( '${counter.value}', style: Theme.of(context).textTheme.display1, ), ), ], ), ), ); } }
counter.dart
import 'package:mobx/mobx.dart'; // Include generated file part 'counter.g.dart'; // This is the class used by rest of your codebase class Counter = _Counter with _$Counter; // The store-class abstract class _Counter implements Store { @observable int value = 0; @action void increment() { value++; } }
感謝你的圍觀,目前是我寫Flutter
遇到的一些坑,歡迎你們一踩坑,你們有什麼意見和建議均可以提出來,謝謝。
圖片上傳失敗...(image-746f90-1557908537541)