B standing video

www.bilibili.com/video/BV1vV… www.bilibili.com/video/BV1SA… www.bilibili.com/video/BV1jt… www.bilibili.com/video/BV1wt… www.bilibili.com/video/BV1b5… www.bilibili.com/video/BV11z…

In this section, the target

  • The welcome page is displayed after the first login
  • Offline login
  • Provider responds to data management
  • APP color gray processing is realized
  • Log out
  • Http Status 401 Authentication and authorization
  • Home disk cache
  • Home page cache policy, delay 1 to 3 seconds
  • Home skeleton screen

video

  • B station
  • Tubing mirror

resources

  • Blue Lake design draft (plus wechat to authorized Ducafecat) lanhuapp.com/url/wbhGq

  • YAPI Interface management yapi.demo.qunar.com/

  • Code github.com/ducafecat/f…

  • reference

    • provider
    • pk_skeleton

The welcome page is displayed for the first time and offline login is performed

  • lib/global.dart
  // open it for the first time
  static bool isFirstOpen = false;

  /// Whether to log in offline
  static bool isOfflineLogin = false;

  /// init
  static Future init() async{...// Read device turned on for the first timeisFirstOpen = ! StorageUtil().getBool(STORAGE_DEVICE_ALREADY_OPEN_KEY);if (isFirstOpen) {
      StorageUtil().setBool(STORAGE_DEVICE_ALREADY_OPEN_KEY, true);
    }

    // Read the offline user information
    var _profileJSON = StorageUtil().getJSON(STORAGE_USER_PROFILE_KEY);
    if(_profileJSON ! =null) {
      profile = UserLoginResponseEntity.fromJson(_profileJSON);
      isOfflineLogin = true;
    }
Copy the code
  • 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(), ); }}Copy the code

Provider implements dynamic gray processing

Pub. Flutter – IO. Cn/packages/pr…

Step 1: Install dependencies

dependencies:
  provider: ^ 4.0.4
Copy the code

Step 2: Create the response data class

  • lib/common/provider/app.dart
import 'package:flutter/material.dart';

/// The corresponding system status
class AppState with ChangeNotifier {
  bool _isGrayFilter;

  get isGrayFilter => _isGrayFilter;

  AppState({bool isGrayFilter = false{})this._isGrayFilter = isGrayFilter; }}Copy the code

Step 3: Initial response data

Method 1: Create a data object and then mount it

  • lib/global.dart
  /// Application status
  static AppState appState = AppState();
Copy the code
  • lib/main.dart
void main() => Global.init().then((e) => runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider<AppState>.value(
            value: Global.appState,
          ),
        ],
        child: MyApp(),
      ),
    ));
Copy the code

Method 2: Create objects during mounting

  • lib/main.dart
void main() => Global.init().then((e) => runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider<AppState>(
            Create: (_) => new AppState(),
          ),
        ],
        child: MyApp(),
      ),
    ));
Copy the code

Step 4: Notify the data of sound changes

  • lib/common/provider/app.dart
class AppState with ChangeNotifier {...// Toggle the grey filter
  switchGrayFilter() {
    _isGrayFilter = !_isGrayFilter;
    notifyListeners();
  }
}
Copy the code

Step 5: Voice change of received data

Method 1: 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 {
            returnMyApp(); }}),),));Copy the code

Method 2: provider.of

  • lib/pages/account/account.dart
    final appState = Provider.of<AppState>(context);

    return Column(
      children: <Widget>[
        MaterialButton(
          onPressed: () {
            appState.switchGrayFilter();
          },
          child: Text('Grey switch${appState.isGrayFilter}'),),,);Copy the code

Multiple response data processing

  • Mount using MultiProvider

  • Receive with Consumer2 ~ Consumer6

Log out

  • lib/common/utils/authentication.dart
// check whether there is a token
Future<bool> isAuthenticated() async {
  var profileJSON = StorageUtil().getJSON(STORAGE_USER_PROFILE_KEY);
  returnprofileJSON ! =null ? true : false;
}

// delete the cache token
Future deleteAuthentication() async {
  await StorageUtil().remove(STORAGE_USER_PROFILE_KEY);
  Global.profile = null;
}

/// Log in again
Future goLoginPage(BuildContext context) async {
  await deleteAuthentication();
  Navigator.pushNamedAndRemoveUntil(
      context, "/sign-in", (Route<dynamic> route) => false);
}
Copy the code
  • 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('users:${Global.profile.displayName}'),
        Divider(),
        MaterialButton(
          onPressed: () {
            goLoginPage(context);
          },
          child: Text('exit'),),,); }}Copy the code

Http Status 401 Authentication and authorization

Dio wraps the interface’s context object 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, }); . }Copy the code

Error handling 401 go to login screen

  • lib/common/utils/http.dart
    // Add interceptor
    dio.interceptors
        .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
      return options; //continue
    }, onResponse: (Response response) {
      return response; // continue
    }, onError: (DioError e) {
      ErrorEntity eInfo = createErrorEntity(e);
      // Error message
      toastInfo(msg: eInfo.message);
      // Error interaction handling
      var context = e.request.extra["context"];
      if(context ! =null) {
        switch (eInfo.code) {
          case 401: // No permission to log in again
            goLoginPage(context);
            break;
          default:}}return eInfo;
    }));
Copy the code

Home disk cache

  • lib/common/utils/net_cache.dart
      // Strategy 1 memory cache first, 2 disk cache second

      // 1 Memory cache
      var ob = cache[key];
      if(ob ! =null) {
        // If the cache is not expired, the cache contents are returned
        if ((DateTime.now().millisecondsSinceEpoch - ob.timeStamp) / 1000 <
            CACHE_MAXAGE) {
          return cache[key].response;
        } else {
          // If it has expired, delete the cache and continue to request the servercache.remove(key); }}// 2 Disk cache
      if (cacheDisk) {
        var cacheData = StorageUtil().getJSON(key);
        if(cacheData ! =null) {
          return Response(
            statusCode: 200, data: cacheData, ); }}Copy the code

Home page cache policy, delay 1 to 3 seconds

  • lib/pages/main/channels_widget.dart
  // If there is disk cache, delay 3 seconds to pull update file
  _loadLatestWithDiskCache() {
    if (CACHE_ENABLE == true) {
      var cacheData = StorageUtil().getJSON(STORAGE_INDEX_NEWS_CACHE_KEY);
      if(cacheData ! =null) {
        Timer(Duration(seconds: 3), () { _controller.callRefresh(); }); }}}Copy the code

Home skeleton screen

Pub. Flutter – IO. Cn/packages/pk…

  • lib/pages/main/main.dart
  @override
  Widget build(BuildContext context) {
    return _newsPageList == null
        ? cardListSkeleton()
        : EasyRefresh(
            enableControlFinishRefresh: true,
            controller: _controller,
            ...
Copy the code