共计 5270 个字符,预计需要花费 14 分钟才能阅读完成。
B 站视频
https://www.bilibili.com/vide…
https://www.bilibili.com/vide…
https://www.bilibili.com/vide…
https://www.bilibili.com/vide…
https://www.bilibili.com/vide…
https://www.bilibili.com/vide…
本节目标
- 第一次登录显示欢迎界面
- 离线登录
- Provider 响应数据管理
- 实现 APP 色彩灰度处理
- 注销登录
- Http Status 401 认证授权
- 首页磁盘缓存
- 首页缓存策略,延迟 1~3 秒
- 首页骨架屏
视频
- b 站
- 油管镜像
资源
- 蓝湖设计稿(加微信给授权 ducafecat)
https://lanhuapp.com/url/wbhGq - YAPI 接口管理
http://yapi.demo.qunar.com/ - 代码
https://github.com/ducafecat/… -
参考
- provider
- pk_skeleton
第一次显示欢迎界面、离线登录
- lib/global.dart
/// 是否第一次打开
static bool isFirstOpen = false;
/// 是否离线登录
static bool isOfflineLogin = false;
/// init
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
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/pac…
步骤 1:安装依赖
dependencies:
provider: ^4.0.4
步骤 2:创建响应数据类
- lib/common/provider/app.dart
import 'package:flutter/material.dart';
/// 系统相应状态
class AppState with ChangeNotifier {
bool _isGrayFilter;
get isGrayFilter => _isGrayFilter;
AppState({bool isGrayFilter = false}) {this._isGrayFilter = isGrayFilter;}
}
步骤 3:初始响应数据
方式一:先创建数据对象,再挂载
- lib/global.dart
/// 应用状态
static AppState appState = AppState();
- lib/main.dart
void main() => Global.init().then((e) => runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<AppState>.value(value: Global.appState,),
],
child: MyApp(),),
));
方式二:挂载时,创建对象
- lib/main.dart
void main() => Global.init().then((e) => runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<AppState>(Create: (_) => new AppState(),),
],
child: MyApp(),),
));
步骤 4:通知数据发声变化
- lib/common/provider/app.dart
class AppState with ChangeNotifier {
...
// 切换灰色滤镜
switchGrayFilter() {
_isGrayFilter = !_isGrayFilter;
notifyListeners();}
}
步骤 5:收到数据发声变化
方式一:Consumer
- lib/main.dart
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
final appState = Provider.of<AppState>(context);
return Column(
children: <Widget>[
MaterialButton(onPressed: () {appState.switchGrayFilter();
},
child: Text('灰色切换 ${appState.isGrayFilter}'),
),
],
);
多个响应数据处理
- 挂载用 MultiProvider
- 接收用 Consumer2 ~ Consumer6
注销登录
- lib/common/utils/authentication.dart
/// 检查是否有 token
Future<bool> isAuthenticated() async {var profileJSON = StorageUtil().getJSON(STORAGE_USER_PROFILE_KEY);
return profileJSON != null ? true : false;
}
/// 删除缓存 token
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
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
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
// 添加拦截器
dio.interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) {return options; //continue}, onResponse: (Response response) {return response; // continue}, 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 然后才是磁盘缓存
// 1 内存缓存
var ob = cache[key];
if (ob != null) {
// 若缓存未过期,则返回缓存内容
if ((DateTime.now().millisecondsSinceEpoch - ob.timeStamp) / 1000 <
CACHE_MAXAGE) {return cache[key].response;
} else {
// 若已过期则删除缓存,继续向服务器请求
cache.remove(key);
}
}
// 2 磁盘缓存
if (cacheDisk) {var cacheData = StorageUtil().getJSON(key);
if (cacheData != null) {
return Response(
statusCode: 200,
data: cacheData,
);
}
}
首页缓存策略,延迟 1~3 秒
- lib/pages/main/channels_widget.dart
// 如果有磁盘缓存,延迟 3 秒拉取更新档案
_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/pac…
- lib/pages/main/main.dart
@override
Widget build(BuildContext context) {
return _newsPageList == null
? cardListSkeleton()
: EasyRefresh(
enableControlFinishRefresh: true,
controller: _controller,
...
正文完