本节目标
- 第一次登录显示欢迎界面
- 离线登录
- Provider 响应数据管理
- 实现 APP 色彩灰度处理
- 注销登录
- Http Status 401 认证授权
- 首页磁盘缓存
- 首页缓存策略,延迟 1~3 秒
- 首页骨架屏
视频
代码
https://github.com/ducafecat/flutter_learn_news/releases/tag/v1.0.7
视频
资源
第一次显示欢迎界面、离线登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| static bool isFirstOpen = false;
static bool isOfflineLogin = false;
static Future init() async { ...
isFirstOpen = !StorageUtil().getBool(STORAGE_DEVICE_ALREADY_OPEN_KEY); if (isFirstOpen) { StorageUtil().setBool(STORAGE_DEVICE_ALREADY_OPEN_KEY, true); }
var _profileJSON = StorageUtil().getJSON(STORAGE_USER_PROFILE_KEY); if (_profileJSON != null) { profile = UserLoginResponseEntity.fromJson(_profileJSON); isOfflineLogin = true; }
|
- lib/pages/index/index.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class IndexPage extends StatefulWidget { IndexPage({Key key}) : super(key: key);
@override _IndexPageState createState() => _IndexPageState(); }
class _IndexPageState extends State<IndexPage> { @override Widget build(BuildContext context) { ScreenUtil.init( context, width: 375, height: 812 - 44 - 34, allowFontScaling: true, );
return Scaffold( body: Global.isFirstOpen == true ? WelcomePage() : Global.isOfflineLogin == true ? ApplicationPage() : SignInPage(), ); } }
|
Provider 实现动态灰度处理
https://pub.flutter-io.cn/packages/provider
步骤 1:安装依赖
1 2
| dependencies: provider: ^4.0.4
|
步骤 2:创建响应数据类
- lib/common/provider/app.dart
1 2 3 4 5 6 7 8 9 10 11 12
| import 'package:flutter/material.dart';
class AppState with ChangeNotifier { bool _isGrayFilter;
get isGrayFilter => _isGrayFilter;
AppState({bool isGrayFilter = false}) { this._isGrayFilter = isGrayFilter; } }
|
步骤 3:初始响应数据
方式一:先创建数据对象,再挂载
1 2
| static AppState appState = AppState();
|
1 2 3 4 5 6 7 8 9 10
| void main() => Global.init().then((e) => runApp( MultiProvider( providers: [ ChangeNotifierProvider<AppState>.value( value: Global.appState, ), ], child: MyApp(), ), ));
|
方式二:挂载时,创建对象
1 2 3 4 5 6 7 8 9 10
| void main() => Global.init().then((e) => runApp( MultiProvider( providers: [ ChangeNotifierProvider<AppState>( Create: (_) => new AppState(), ), ], child: MyApp(), ), ));
|
步骤 4:通知数据发声变化
- lib/common/provider/app.dart
1 2 3 4 5 6 7 8 9
| class AppState with ChangeNotifier { ...
switchGrayFilter() { _isGrayFilter = !_isGrayFilter; notifyListeners(); } }
|
步骤 5:收到数据发声变化
方式一:Consumer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void main() => Global.init().then((e) => runApp( MultiProvider( providers: [ ChangeNotifierProvider<AppState>.value( value: Global.appState, ), ], child: Consumer<AppState>(builder: (context, appState, _) { if (appState.isGrayFilter) { return ColorFiltered( colorFilter: ColorFilter.mode(Colors.white, BlendMode.color), child: MyApp(), ); } else { return MyApp(); } }), ), ));
|
方式二:Provider.of
- lib/pages/account/account.dart
1 2 3 4 5 6 7 8 9 10 11 12
| final appState = Provider.of<AppState>(context);
return Column( children: <Widget>[ MaterialButton( onPressed: () { appState.switchGrayFilter(); }, child: Text('灰色切换 ${appState.isGrayFilter}'), ), ], );
|
多个响应数据处理
注销登录
- lib/common/utils/authentication.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Future<bool> isAuthenticated() async { var profileJSON = StorageUtil().getJSON(STORAGE_USER_PROFILE_KEY); return profileJSON != null ? true : false; }
Future deleteAuthentication() async { await StorageUtil().remove(STORAGE_USER_PROFILE_KEY); Global.profile = null; }
Future goLoginPage(BuildContext context) async { await deleteAuthentication(); Navigator.pushNamedAndRemoveUntil( context, "/sign-in", (Route<dynamic> route) => false); }
|
- lib/pages/account/account.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class _AccountPageState extends State<AccountPage> { @override Widget build(BuildContext context) { final appState = Provider.of<AppState>(context);
return Column( children: <Widget>[ Text('用户: ${Global.profile.displayName}'), Divider(), MaterialButton( onPressed: () { goLoginPage(context); }, child: Text('退出'), ), ], ); } }
|
Http Status 401 认证授权
dio 封装界面的上下文对象 BuildContext context
- lib/common/utils/http.dart
1 2 3 4 5 6 7 8 9 10 11 12
| Future post( String path, { @required BuildContext context, dynamic params, Options options, }) async { Options requestOptions = options ?? Options(); requestOptions = requestOptions.merge(extra: { "context": context, }); ... }
|
错误处理 401 去登录界面
- lib/common/utils/http.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| dio.interceptors .add(InterceptorsWrapper(onRequest: (RequestOptions options) { return options; }, onResponse: (Response response) { return response; }, onError: (DioError e) { ErrorEntity eInfo = createErrorEntity(e); toastInfo(msg: eInfo.message); var context = e.request.extra["context"]; if (context != null) { switch (eInfo.code) { case 401: goLoginPage(context); break; default: } } return eInfo; }));
|
首页磁盘缓存
- lib/common/utils/net_cache.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
var ob = cache[key]; if (ob != null) { if ((DateTime.now().millisecondsSinceEpoch - ob.timeStamp) / 1000 < CACHE_MAXAGE) { return cache[key].response; } else { cache.remove(key); } }
if (cacheDisk) { var cacheData = StorageUtil().getJSON(key); if (cacheData != null) { return Response( statusCode: 200, data: cacheData, ); } }
|
首页缓存策略,延迟 1~3 秒
- lib/pages/main/channels_widget.dart
1 2 3 4 5 6 7 8 9 10 11
| _loadLatestWithDiskCache() { if (CACHE_ENABLE == true) { var cacheData = StorageUtil().getJSON(STORAGE_INDEX_NEWS_CACHE_KEY); if (cacheData != null) { Timer(Duration(seconds: 3), () { _controller.callRefresh(); }); } } }
|
首页骨架屏
https://pub.flutter-io.cn/packages/pk_skeleton
1 2 3 4 5 6 7 8
| @override Widget build(BuildContext context) { return _newsPageList == null ? cardListSkeleton() : EasyRefresh( enableControlFinishRefresh: true, controller: _controller, ...
|
© 猫哥
https://ducafecat.tech
邮箱 ducafecat@gmail.com / 微信 ducafecat / 留言板 disqus