說(shuō)明
目前增加了路由跳轉(zhuǎn),可以帶參數(shù)跳轉(zhuǎn)頁(yè)面姆钉。下拉可以自定義刷新樣式说订,IOS點(diǎn)擊Status Bar
回到頂部,目前已經(jīng)測(cè)試過(guò)潮瓶。狀態(tài)管理器使用Mobx
,我自己覺(jué)得對(duì)于Redux
使用起來(lái)會(huì)復(fù)雜一點(diǎn)陶冷,下面是提供的預(yù)覽GIF圖,卡頓現(xiàn)象是因?yàn)槠聊讳浿频膸视悬c(diǎn)低。
項(xiàng)目地址:https://github.com/Tecode/flutter_book,不定時(shí)的更新毯辅,歡迎start埂伦。
安卓預(yù)覽
IOS預(yù)覽
依賴(lài)庫(kù)
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依賴(lài)
mobx_codegen: // Mobx依賴(lài)
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)
修改系統(tǒng)狀態(tài)欄顏色
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() {
// 修改系統(tǒng)狀態(tài)欄顏色
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: Color(AppColors.themeColor), // navigation bar color
statusBarColor: Color(AppColors.themeColor), // status bar color
));
runApp(MyApp());wenti
}
自定義appBar左側(cè)導(dǎo)航顯示的內(nèi)容
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");
},
)
...
)
媒體查詢(xún)
MediaQuery.of(context)
資源配置
assets:
- assets/icon/
- lib/containers/
- lib/model/
- lib/helpers/
- lib/routers/
- assets/images/
路由配置
這里我使用的是fluro
配置路由,這里我偷一下懶了思恐,就沒(méi)有使用原生的方法沾谜,不過(guò)他幫我們封裝了好多的方法我們可以很方便的去使用它,下面說(shuō)一下路由的配置胀莹。
lib\routers\routers.dart
配置路由對(duì)應(yīng)的模塊基跑,可以理解成Vue-router
或React-router
一樣,先要將對(duì)應(yīng)的路由配置到你要跳轉(zhuǎn)的模塊去描焰。
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
在這里可以處理一些傳過(guò)來(lái)的參數(shù)媳否,然后我們將參數(shù)放入類(lèi)中實(shí)例化。
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('熱門(mén)圖書(shū)')}",
transition: TransitionType.native
);
路由傳參
路由不支持中文字符需要編碼再解碼
import 'dart:convert';
/// fluro 參數(shù)編碼解碼工具類(lèi)
class FluroConvertUtils {
/// fluro 傳遞中文參數(shù)前篱竭,先轉(zhuǎn)換,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 傳遞后取出參數(shù)步绸,解析
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('熱門(mén)圖書(shū)')}",
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狀態(tài)管理器
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:
多個(gè)頁(yè)面使用一個(gè)store
這里要使用到provider: ^2.0.1
掺逼,類(lèi)似React
的Provider
。使用Provider
來(lái)包裹我們的組件瓤介,使Mobx
和我們的React
聯(lián)系起來(lái)吕喘。
React Provider
<Provider {...store}>
<Router history={browserHistory}
<App />
</Router>
</Provider>
Dart Provider
Dart Provider
也是一樣的道理,將Mobx
和Flutter
聯(lián)系起來(lái)刑桑,lib/main.dart
完整代碼兽泄,這樣使用可以保證你實(shí)例化的的store
是同一個(gè)類(lèi)。
runApp(MultiProvider(
providers: [
Provider<FindStore>(
builder: (_) => FindStore(),
)
],
child: MyApp(),
));
如何使用
我的導(dǎo)航發(fā)現(xiàn)那一欄和下面的內(nèi)容是分開(kāi)的漾月,當(dāng)我點(diǎn)擊導(dǎo)航的切換按鈕就會(huì)改變顯示的頁(yè)面,這樣我們可以復(fù)用顯示層的UI
組件胃珍,數(shù)據(jù)放專(zhuān)門(mén)的文件去管理梁肿。
來(lái)看看如何實(shí)現(xiàn)的
通過(guò)點(diǎn)擊然后改變數(shù)據(jù)findStore.setTile('tile', true);
導(dǎo)航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("發(fā)現(xiàn)"),
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,
),
);
}
}
內(nèi)容lib/containers/Find.dart
檢測(cè)到數(shù)據(jù)發(fā)生變化蜓陌,頁(yè)面重新渲染得到新的頁(yè)面
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++;
}
注意
如果你是很多個(gè)頁(yè)面共享一個(gè)Store
不要直接導(dǎo)入然后實(shí)例化,例如:
第一個(gè)頁(yè)面 demo1.dart
這個(gè)頁(yè)面我們導(dǎo)入了counter.dart
這個(gè)store
而且我們將它實(shí)例化,當(dāng)我們點(diǎn)擊的時(shí)候數(shù)據(jù)發(fā)生變化頁(yè)面會(huì)重新渲染
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(
'數(shù)值是:',
),
// 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),
),
);
}
}
第二個(gè)頁(yè)面 demo2.dart
這個(gè)頁(yè)面我們也導(dǎo)入了counter.dart
吩蔑,我們要的結(jié)果是第一個(gè)頁(yè)面的數(shù)據(jù)變化了也影響這個(gè)頁(yè)面钮热,但是顯然是不能的。因?yàn)?code>store雖然是一個(gè)烛芬,但是實(shí)例化的時(shí)候是兩個(gè)不同的隧期,所以第一個(gè)頁(yè)面的數(shù)據(jù)變化了也不會(huì)影響到這里。
怎么解決呢赘娄?我們可以使用之前提到的Provider
去將Mobx
與Flutter
聯(lián)系起來(lái)然后通過(guò)上下關(guān)系去的到我們想要的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(
'第二個(gè)頁(yè)面顯示第一個(gè)頁(yè)面的數(shù)是:',
),
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++;
}
}
正確的使用方法
結(jié)束語(yǔ)
感謝你的圍觀,目前是我寫(xiě)Flutter
遇到的一些坑遣臼,歡迎大家一踩坑性置,大家有什么意見(jiàn)和建議都可以提出來(lái),謝謝揍堰。