Flutter+Mobx实战,写一个App应用
栏目: JavaScript · 发布时间: 5年前
内容简介:目前增加了路由跳转,可以带参数跳转页面。下拉可以自定义刷新样式,IOS点击项目地址:这里我使用的是
目前增加了路由跳转,可以带参数跳转页面。下拉可以自定义刷新样式,IOS点击 Status Bar
回到顶部,目前已经测试过。状态管理器使用 Mobx
,我自己觉得对于 Redux
使用起来会复杂一点,下面是提供的预览GIF图,卡顿现象是因为屏幕录制的帧率有点低。
项目地址: github.com/Tecode/flut… ,不定时的更新,欢迎start。
安卓预览
IOS预览
依赖库
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: 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
配置路由,这里我偷一下懒了,就没有使用原生的方法,不过他帮我们封装了好多的方法我们可以很方便的去使用它,下面说一下路由的配置。
lib\routers\routers.dart
配置路由对应的模块,可以理解成 Vue-router
或 React-router
一样,先要将对应的路由配置到你要跳转的模块去。
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
在这里可以处理一些传过来的参数,然后我们将参数放入类中实例化。
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
绑定,这样你的路由就可以生效了
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)); }); 复制代码
使用Mobx状态管理器
pubspec.yaml配置
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: 复制代码
多个页面使用一个store
这里要使用到 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
组件,数据放专门的文件去管理。
来看看如何实现的
通过点击然后改变数据 findStore.setTile('tile', true);
导航 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, ), ), ], ), ), ); } } 复制代码
公共的Store 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+Mobx实战,写一个App应用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- go语言实战教程:Redis实战项目应用
- 单页应用的HATEOAS实战
- 单页应用的HATEOAS实战
- Vue应用框架整合与实战
- Spark综合使用及电商案例实战精析-Spark商业应用实战
- Spark Streaming--应用与实战(一)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。