Flutter+Mobx实战,写一个App应用
栏目: JavaScript · 发布时间: 6年前
内容简介:目前增加了路由跳转,可以带参数跳转页面。下拉可以自定义刷新样式,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--应用与实战(一)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Rework
Jason Fried、David Heinemeier Hansson / Crown Business / 2010-3-9 / USD 22.00
"Jason Fried and David Hansson follow their own advice in REWORK, laying bare the surprising philosophies at the core of 37signals' success and inspiring us to put them into practice. There's no jarg......一起来看看 《Rework》 这本书的介绍吧!