B standing video

www.bilibili.com/video/BV1A5…

In this section, the target

  • Global data, response data, persistence
  • HTTP get cache
  • The HTTP proxy agent
  • A string grab tool
  • Iconfont font library
  • Main interface construction
  • BottomNavigationBar navigation control
  • Write API interface code

Client data management

The data type

  • Global data

Store in memory

User data, language packs

  • The response data

Store in memory

User login status, multiple languages, skin style

Redux, Bloc, Provider

  • persistence

APP stays on disk

Browser cookie localStorage

Write global management

  • lib/global.dart
/// global configuration
class Global {
  /// User configuration
  static UserLoginResponseEntity profile = UserLoginResponseEntity(
    accessToken: null,);/ / / whether the release
  static bool get isRelease => bool.fromEnvironment("dart.vm.product");

  /// init
  static Future init() async {
    // Run initial
    WidgetsFlutterBinding.ensureInitialized();

    // Tool initialization
    await StorageUtil.init();
    HttpUtil();

    // Read the offline user information
    var _profileJSON = StorageUtil().getJSON(STORAGE_USER_PROFILE_KEY);
    if(_profileJSON ! =null) {
      profile = UserLoginResponseEntity.fromJson(_profileJSON);
    }

    / / HTTP cache

    // Android status bar for transparent immersion
    if(Platform.isAndroid) { SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent); SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); }}// Persist user information
  static Future<bool> saveProfile(UserLoginResponseEntity userResponse) {
    profile = userResponse;
    returnStorageUtil() .setJSON(STORAGE_USER_PROFILE_KEY, userResponse.toJson()); }}Copy the code

Call running

  • lib/main.dart
void main() => Global.init().then((e) => runApp(MyApp()));

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    returnContainer(); }}Copy the code

Http memory cache

Caching strategies

code

  • Dart cache utility class lib/common/utils/net_cache.dart
import 'dart:collection';

import 'package:dio/dio.dart';
import 'package:flutter_ducafecat_news/common/values/values.dart';

class CacheObject {
  CacheObject(this.response)
      : timeStamp = DateTime.now().millisecondsSinceEpoch;
  Response response;
  int timeStamp;

  @override
  bool operator ==(other) {
    return response.hashCode == other.hashCode;
  }

  @override
  int get hashCode => response.realUri.hashCode;
}

class NetCache extends Interceptor {
  // To ensure that the iterator order is consistent with the object insertion time order, we use LinkedHashMap
  var cache = LinkedHashMap<String, CacheObject>();

  @override
  onRequest(RequestOptions options) async {
    if(! CACHE_ENABLE)return options;

    // refresh flag is "pull refresh"
    bool refresh = options.extra["refresh"] = =true;

    // In the case of pull-down refresh, delete related caches first
    if (refresh) {
      if (options.extra["list"] = =true) {
        // All caches in the url containing the current path will be removed.
        cache.removeWhere((key, v) => key.contains(options.path));
      } else {
        // If it is not a list, only caches with the same URI are removed
        delete(options.uri.toString());
      }
      return options;
    }

    // get requests to enable caching
    if (options.extra["noCache"] != true &&
        options.method.toLowerCase() == 'get') {
      String key = options.extra["cacheKey"]???? options.uri.toString();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); }}}}@override
  onError(DioError err) async {
    // Error status is not cached
  }

  @override
  onResponse(Response response) async {
    // If caching is enabled, the returned result is saved to the cache
    if (CACHE_ENABLE) {
      _saveCache(response);
    }
  }

  _saveCache(Response object) {
    RequestOptions options = object.request;

    // Only get requests are cached
    if (options.extra["noCache"] != true &&
        options.method.toLowerCase() == "get") {
      // If the number of caches exceeds the maximum number, the earliest record is removed first
      if (cache.length == CACHE_MAXCOUNT) {
        cache.remove(cache[cache.keys.first]);
      }
      String key = options.extra["cacheKey"] ?? options.uri.toString();
      cache[key] = CacheObject(object);
    }
  }

  void delete(Stringkey) { cache.remove(key); }}Copy the code
  • Dio encapsulation lib/common/utils/HTTP. Dart
  // Add memory cacheHttpUtil._internal() { ... dio.interceptors.add(NetCache()); . }// Modify the GET request
  /// Restful get
  /// refresh Whether to pull-down refresh The default value is false
  /// noCache noCache the default value is true
  /// list Whether to list the default value is false
  /// cacheKey specifies the cacheKey
  Future get(
    String path, {
    dynamic params,
    Options options,
    bool refresh = false.boolnoCache = ! CACHE_ENABLE,bool list = false.String cacheKey,
  }) async {
    try {
      Options requestOptions = options ?? Options();
      requestOptions = requestOptions.merge(extra: {
        "refresh": refresh,
        "noCache": noCache,
        "list": list,
        "cacheKey": cacheKey,
      });
      Map<String.dynamic> _authorization = getAuthorizationHeader();
      if(_authorization ! =null) {
        requestOptions = requestOptions.merge(headers: _authorization);
      }

      var response = await dio.get(path,
          queryParameters: params,
          options: requestOptions,
          cancelToken: cancelToken);
      return response.data;
    } on DioError catch (e) {
      throwcreateErrorEntity(e); }}Copy the code

Http Proxy Proxy + string capture

Install the Fiddle

www.telerik.com/download/fi…

Dio join proxy

  • lib/common/utils/http.dart
  if(! Global.isRelease && PROXY_ENABLE) { (dio.httpClientAdapteras DefaultHttpClientAdapter).onHttpClientCreate =
        (client) {
      client.findProxy = (uri) {
        return "PROXY $PROXY_IP:$PROXY_PORT";
      };
      // The proxy tool will provide a self-signed certificate for capturing packets, which will fail certificate verification, so we disable certificate verification
      client.badCertificateCallback =
          (X509Certificate cert, String host, int port) => true;
    };
  }
Copy the code

Iconfont font library

The introduction of the process

  • The login

www.iconfont.cn

  • Create font project

  • Font file in

assets/fonts/iconfont.ttf

  • pubspec.yaml
  fonts:
    .
    - family: Iconfont
      fonts:
        - asset: assets/fonts/iconfont.ttf
Copy the code
  • lib/common/utils/iconfont.dart
import 'package:flutter/material.dart';

class Iconfont {
    // iconName: share
  static const share = IconData(
    0xe60d,
    fontFamily: 'Iconfont',
    matchTextDirection: true,); . }Copy the code

Automatically generate font library code

Github.com/ymzuiku/ico…

  • Pull project, compile
# Pull item
> git clone https://github.com/ymzuiku/iconfont_builder

# update
> pub get

# Install tools
> pub global activate iconfont_builder

Check the environment configuration
export PATH=${PATH}:~/.pub-cache/bin
Copy the code
  • Refer to my configuration
# flutter sdk
export PATH=${PATH}:~/Documents/sdk/flutter/bin

# dart sdk
export PATH=${PATH}:~/Documents/sdk/flutter/bin/cache/dart-sdk/bin
export PATH=${PATH}:~/.pub-cache/bin

# flutter- IO domestic mirror
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

# android
export ANDROID_HOME=~/Library/Android/sdk
export PATH=${PATH}:${ANDROID_HOME}/platform-tools
export PATH=${PATH}:${ANDROID_HOME}/tools
Copy the code
  • Generate font classes
cdThe root directory of your project iconfont_builder - from/assets/fonts - to.. / lib/common/utils/iconfont dartCopy the code

Write API business code

  • Yapi configuration

Import doc/API. Json

  • code

Build the main interface frame

  • Framework page lib/pages/application/application. The dart
.class _ApplicationPageState extends State<ApplicationPage>
    with SingleTickerProviderStateMixin {
  // The current TAB page number
  int _page = 0;
  // TAB page title
  final List<String> _tabTitles = [
    'Welcome'.'Cagegory'.'Bookmarks'.'Account'
  ];
  // Page controller
  PageController _pageController;

  // Bottom navigation project
  final List<BottomNavigationBarItem> _bottomTabs = <BottomNavigationBarItem>[...] ;// TAB animation
  void _handleNavBarTap(int index) {
    ...
  }

  // TAB page number switch
  void _handlePageChanged(int page) {
    ...
  }

  // Top navigation
  Widget _buildAppBar() {
    return Container();
  }

  / / content pages
  Widget _buildPageView() {
    return Container();
  }

  // Bottom navigation
  Widget _buildBottomNavigationBar() {
    return Container();
  }

  @override
  Widget build(BuildContext context) {
    returnScaffold( appBar: _buildAppBar(), body: _buildPageView(), bottomNavigationBar: _buildBottomNavigationBar(), ); }}Copy the code

Write home page code

  • The home page is lib/pages/main/main.dart
.class _MainPageState extends State<MainPage> {
  NewsPageListResponseEntity _newsPageList; // Turn the news page
  NewsRecommendResponseEntity _newsRecommend; // News recommendation
  List<CategoryResponseEntity> _categories; / / classification
  List<ChannelResponseEntity> _channels; / / channel

  String _selCategoryCode; // The selected category Code

  @override
  void initState() {
    super.initState();
    _loadAllData();
  }

  // Read all data
  _loadAllData() async{... }// Categorize the menu
  Widget _buildCategories() {
    return Container();
  }
  // Implement business before extraction

  // Recommended reading
  Widget _buildRecommend() {
    return Container();
  }

  / / channel
  Widget _buildChannels() {
    return Container();
  }

  // News list
  Widget _buildNewsList() {
    return Container();
  }

  // AD banners
  // Subscribe by email
  Widget _buildEmailSubscribe() {
    return Container();
  }

  @override
  Widget build(BuildContext context) {
    returnSingleChildScrollView( child: Column( children: <Widget>[ _buildCategories(), _buildRecommend(), _buildChannels(), _buildNewsList(), _buildEmailSubscribe(), ], ), ); }}Copy the code
  • Extraction of news classification lib/pages/main/categories_widget dart
Widget newsCategoriesWidget(
    {List<CategoryResponseEntity> categories,
    String selCategoryCode,
    Function(CategoryResponseEntity) onTap}) {
  return categories == null
      ? Container()
      : SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: Row(
            children: categories.map<Widget>((item) {
              return Container(
                alignment: Alignment.center,
                height: duSetHeight(52),
                padding: EdgeInsets.symmetric(horizontal: 8),
                child: GestureDetector(
                  child: Text(
                    item.title,
                    style: TextStyle(
                      color: selCategoryCode == item.code
                          ? AppColors.secondaryElementText
                          : AppColors.primaryText,
                      fontSize: duSetFontSize(18),
                      fontFamily: 'Montserrat',
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                  onTap: () => onTap(item),
                ),
              );
            }).toList(),
          ),
        );
}
Copy the code

Blue Lake design draft

lanhuapp.com/url/lYuz1 Password: gSKl

Blue Lake now charges, so please upload xD design draft by yourself to check the mark. Commercial design draft file is not easy to share directly, you can add wechat to contact Ducafecat

YAPI interface management

yapi.demo.qunar.com/

Git code

Github.com/ducafecat/f…

tool

  • json to object quicktype
  • Fiddler caught
  • Iconfont Ali icon library
  • Iconfont generation tool

VSCode plug-in

  • Flutter, Dart
  • Flutter Widget Snippets
  • Awesome Flutter Snippets
  • Paste JSON as Code
  • bloc

video

  • B station
  • Tubing mirror