diff --git a/source/_posts/flutter/Flutter动态列表.md b/source/_posts/flutter/Flutter动态列表.md index 41dd232..10e0877 100644 --- a/source/_posts/flutter/Flutter动态列表.md +++ b/source/_posts/flutter/Flutter动态列表.md @@ -116,3 +116,14 @@ post的调用方式如下 ```dart http.Response res = await http.post(url, body: params); ``` + +#### JSON解析 +需要引入`dart:convert`模块 + +```dart +import 'dart:convert'; + +var data = json.decode(jsonStr); +print(data['total']); +``` +JSON的序列化使用`json.encode`即可 diff --git a/source/_posts/flutter/Flutter路由.md b/source/_posts/flutter/Flutter路由.md new file mode 100644 index 0000000..ef4afc7 --- /dev/null +++ b/source/_posts/flutter/Flutter路由.md @@ -0,0 +1,253 @@ +--- +title: Flutter路由 +date: 2019-11-10 10:06:41 +tags: + - flutter + - dart +categories: + - flutter +--- + +使用`MaterialPageRoute`也就是页面路由,可以实现不同Widget之间的跳转 +页面只是一个全屏的Widget + + +先写好另一个页面的Widget +```dart +class DetailPage extends StatefulWidget { + DetailPage({Key key, this.title}) : super(key: key); + final String title; + @override + _DetailPageState createState() => _DetailPageState(); +} + +class _DetailPageState extends State { + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Text('这里是另一个页面') + ); + } +} +``` +简单的页面跳转 +```dart +Navigator.push(context, MaterialPageRoute(builder: (_){ + return DetailPage(title: 'detail page'); +})); +``` +既然是push,那么就是个栈模型了,跳转到另一个页面相当于入栈 +如果要返回到上一级页面可以这样做,pop相当于是出栈 +```dart +Navigator.pop(context); +``` +### 命名路由 +可以在MaterialApp当中传入routes参数,该参数是一个Map对象 +key是路由名称,value是返回组件对象的函数 +```dart +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.brown, + ), + routes: { + '/' : (context) => MyHomePage(title: 'Flutter Demo Home Page'), + '/detail' : (context) => DetailPage(title: 'detail page') + }, + initialRoute: '/', + ); + } +} +``` +> 通常的习惯当然是把routes这部分封装在一个单独的模块当中引入使用 + +命名路由的跳转 +```dart +Navigator.pushNamed(context, '/detail'); +``` +这种方式不需要反复构建和销毁组件对象,相对来讲是更好的一种方式 + +### 路由传参 +基本的**Navigator.push**的方式是构建组件,当然可以给组件的构造方法传入参数,比如上面传入的title +这个就不需要特别的传参方式了,如果使用命名路由跳转 +可以传入第三个可选参数arguments +```dart +Navigator.pushNamed(context, '/detail', arguments: {'id': 1001}); +``` +arguments可以是任意的Object + +接收参数方式 +```dart +ModalRoute.of(context).settings.arguments +``` +当然不论传入的是什么,此时获取到的还是Object类型,需要使用`as Map`(取决于传入的类型而执行的类型推导) +进行强制类型转换 + + +### 组件之间的共享状态 +除了组件之间的传参,以及路由的传参之外,组件之间通常需要有一些状态需要共享 +比如一个常见的需求是用户登录之后,在每个页面上都能看到当前登录的用户是什么名字 + +需要使用到的是`provider`这个第三方模块 + +先定义Model +```dart +// user.dart +import 'package:json_annotation/json_annotation.dart'; + +part 'user.g.dart'; + +@JsonSerializable() +class User { + User({this.username, this.createdAt, this.updatedAt}); + + String username; + String createdAt; + String updatedAt; + + factory User.fromJson(Map json) => _$UserFromJson(json); + Map toJson() => _$UserToJson(this); +} +``` +还有 +```dart +// user.g.dart +part of 'user.dart'; + +User _$UserFromJson(Map json) { + return User( + username: json['username'] as String, + createdAt: json['createdAt'] as String, + updatedAt: json['updatedAt'] as String, + ); +} + +Map _$UserToJson(User instance) => { + 'username': instance.username, + 'createdAt': instance.createdAt, + 'updatedAt': instance.updatedAt, +}; +``` +为了能够在启动时读取上次运行的缓存 +需要使用`shared_preferences`这个第三方模块 + +定义全局对象 +```dart +// global.dart +class Global { + static SharedPreferences _prefs; + static Profile profile = Profile(); + + // 是否为release版 + static bool get isRelease => bool.fromEnvironment("dart.vm.product"); + + //初始化全局信息,会在APP启动时执行 + static Future init() async { + _prefs = await SharedPreferences.getInstance(); + var _profile = _prefs.getString("profile"); + if (_profile != null) { + try { + profile = Profile.fromJson(jsonDecode(_profile)); + } catch (e) { + print(e); + } + } + + // 如果没有缓存策略,设置默认缓存策略 + profile.cache = profile.cache ?? CacheConfig() + ..enable = true + ..maxAge = 3600 + ..maxCount = 100; + } + + // 持久化Profile信息 + static saveProfile() => + _prefs.setString("profile", jsonEncode(profile.toJson())); +} + +// profile.dart +@JsonSerializable() +class Profile { + Profile({this.user, this.token, this.theme, this.cache, this.lastLogin, + this.locale}); + + User user; + String token; + CacheConfig cache; + String lastLogin; + String locale; + + factory Profile.fromJson(Map json) => _$ProfileFromJson(json); + Map toJson() => _$ProfileToJson(this); +} +``` +profile当中可以持有User对象 +这种情况下就需要在应用启动时就初始化Global对象 +```dart +void main() => Global.init().then((e) => runApp(MyApp())); +``` +自定义Notifier +```dart +class ProfileChangeNotifier extends ChangeNotifier { + Profile get _profile => Global.profile; + + @override + void notifyListeners() { + Global.saveProfile(); //保存Profile变更 + super.notifyListeners(); //通知依赖的Widget更新 + } +} + +/// 用户状态在登录状态发生变化时更新、通知其依赖项 +class UserModel extends ProfileChangeNotifier { + User get user => _profile.user; + + // APP是否登录(如果有用户信息,则证明登录过) + bool get isLogin => user != null; + + //用户信息发生变化,更新用户信息并通知依赖它的子孙Widgets更新 + set user(User user) { + if (user?.username != _profile.user?.username) { + _profile.lastLogin = _profile.user?.username; + _profile.user = user; + notifyListeners(); + } + } +} +``` +根组件需要使用InheritedProvider进行包装 +```dart +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return InheritedProvider( + value: UserModel(), + child: MaterialApp( + title: 'My App', + theme: ThemeData( + primarySwatch: Colors.pink, + ), + routes: { + 'login' : (context) => Login(title: '登录'), + 'home' : (context) => Home(title: '首页') + }, + initialRoute: 'login', + ), + ); + } +} +``` + +共享状态的更新,比如在登录时,需要把当前用户的信息写入profile,并且传播到各个组件当中 +这里简单写入一个username作为例子 +```dart +Provider.of(context).user = User.fromJson({'username': _usernameController.text}); +``` \ No newline at end of file