1

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 秒
  • 首页骨架屏

视频

资源

第一次显示欢迎界面、离线登录

  • 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,
            ...

独立开发者_猫哥
666 声望126 粉丝