The author of this article: Cherry689, unauthorized reproduction is prohibited.

preface

This issue is divided into three parts:

  • background
  • Engineering efficiency improvement practice
  • conclusion

The background,

2019 was definitely the year that the Flutter technology was in full swing. The adoption of Flutter reduces the manpower input by half under the condition that the demand rhythm is unchanged, which plays an obvious role in relieving the pressure of business r&d. The overall performance and stability of the application is basically the same as that of Native; With its excellent capabilities across multiple platforms, Flutter technology has become the focus of more and more industry partners. From the perspective of engineering system, the scaffolding flutter- Zycli – APP provides a set of standardized API capabilities to standardize and abstract the basic capabilities of the mobile terminal, so that the business as little as possible or even don’t care about the platform differences, focus on business; At the same time, with the help of standardized API capabilities, it can realize cross-terminal and multi-platform deployment, so that the technology can truly enable the rapid development of the company’s business.

Attached is the horizontal comparison industry open source solution:

2. Engineering efficiency improvement practice

Solve the pain point

  • Multiple key projects of a hundred flowers bloom, the realization of the same function technology for many times
  • The standard is not uniform, the late maintenance cost is high, and the r&d personnel are stuck in fixed projects
  • It is necessary to develop unified project development standards, provide basic capabilities, and improve development efficiency

I understand scaffolding.

  • A directory template that can quickly help me generate new projects
  • Can improve my development efficiency and development comfort

Built-in integration

  • Universal UI component library for mobile
  • Mobile base library
  • routing
  • internationalization
  • Subject switch
  • Event bus
  • Storage management
  • State management
  • network
  • The user center
  • Configuration center
  • Screen adaptation
  • AD pages
  • Guide page

The directory structure

android/ 		# Android Project
ios/     		# the ios project
lib/
  |- components/ 	Common Widget component encapsulation
  |- config/ 		# Global configuration parameter
  |- constants/ 	# Constant folder
  |- event_bus/ 	# Event bus
  |- provider/ 		Global state management
  |- pages/ 		# Page UI layer, each complete page, each page can be independent of their own provider state management
      |- AppHomePage/ 	# APP main page
      |- SplashPage/ 	# APP splash screen
  |- service/ 		# Request interface extraction layer
  |- routes/ 		# Define routing related folders
  |- utils/ 		# Public method extract
    |- dio/ 		# Dio Low-level request encapsulation
  |- main.dart 		# Import file
pubspec.yaml 		# Config file
Copy the code

Universal UI component library for mobile

Flutter is a powerful UI expressive tool that helps developers develop cool UIs quickly, efficiently and cheaply, helping businesses build expressive pages. The company wants to quickly occupy the market through products, and at the same time have more identification and style, we need to set our own universal MOBILE TERMINAL UI standard, realize UI standardization, unify the three terminals, and improve delivery efficiency.

All supported components

The serial number Component name describe screenshots
1 Buttons Button to display text, images. Flat buttons and floating buttons are the two most commonly used button types.
2 Badges Message red dot.
3 Appbar A Material Design application bar that consists of toolbars and other possible widgets such as TabBar and FlexibleSpaceBar.
4 BottomNavigationBar The bottom navigation TAB TAB makes it easy to browse and switch between different views.
5 TabBar Top TAB bar.
6 Pickers Bottom wheel selector.
7 Dialogs Dialog boxes are used to prompt the user to make some decisions, or to provide some additional information needed to complete a task.
8 Toast Mainly used for message prompt.
9 Switch IOS style switch. For single state on/off.
10 PopupMenu Bottom popover.
11 SearchBar Search bar.
12 ListTile Form presentation.
13 Notification The notification bar.
14 StateWidget Example of default pages.
15 SelectListTile A cell that interacts with information such as Picker.
16 InputListTitle Form input.
17 Refresh Pull down to refresh and pull up to load.
18 ShareWidget Share panels.
19 ActionSheet Bottom popover, fixed number of rows, not sliding.

Mobile base library

We will sink the base capability into the ZY_base repository for use by all project dependencies.

Such as:

  • Event bus
  • State management
  • routing
  • Screen adaptation
  • Subject switch
  • kit
  • Public page
  • .

routing

Simply speaking, the route is a transfer station, which can be mapped to the corresponding class through the URL, and then jump and carry the parameters required by the page. The function of the route is not only to do the page jump, but also to decouple the file import and jump logic code of the page jump.

It is recommended to put multiple level-1 pages on the App into one zy-main-page. Dart class and configure multiple level-1 pages in a unified way to make the code in Main. dart more clear.

As follows:

import 'package:flutter/material.dart';
import 'package:flutter_zycli_app/constants/zy_r.dart';
import 'package:flutter_zycli_app/pages/home/zy_home_page.dart';
import 'package:flutter_zycli_app/pages/mine/zy_mine_page.dart';
import 'package:flutter_zycli_app/pages/social/zy_social_page.dart';

class ZyMainPage extends StatefulWidget {
  @override
  _ZyMainPageState createState() => _ZyMainPageState();
}

class _ZyMainPageState extends State<ZyMainPage> {
  var _pageController = PageController();

  /// The index of the selected page, which defaults to 0
  int _selectedIndex = 0;

  /// Record the last time the return key was clicked
  DateTime _lastPressed;

  /// Page array
  final List<Widget> pages = <Widget>[
    /// Home page
    ZyHomePage(),

    /// community
    ZySocialPage(),

    /// my
    ZyMinePage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: WillPopScope(
        /// The details are processed when clicking on pop, and a quick click returns only once
        onWillPop: () async {
          if (_lastPressed == null ||
              DateTime.now().difference(_lastPressed) > Duration(seconds: 1)) {
            /// If the interval between two clicks is more than 1 second, the timer will be reset
            _lastPressed = DateTime.now();
            return false;
          }
          return true;
        },
        child: PageView.builder(
          itemBuilder: (ctx, index) => pages[index],
          itemCount: pages.length,
          controller: _pageController,
          physics: NeverScrollableScrollPhysics(),
          onPageChanged: (index) {
            setState(() {
              _selectedIndex = index;
            });
          },
        ),
      ),
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        items: _buildBottomBar(context),
        currentIndex: _selectedIndex,
        onTap: (index) {
          debugPrint('ZyMainPage BottomNavigationBar selected index:$index');
          if (_selectedIndex == 0&& _selectedIndex == index) { _pageController? .notifyListeners(); } _pageController.jumpToPage(index); },),); }List<BottomNavigationBarItem> _buildBottomBar(BuildContext context) {
    return <BottomNavigationBarItem>[
      BottomNavigationBarItem(
        icon: Image.asset(R.TAB_HOME),
        activeIcon: Image.asset(R.TAB_HOME_HIGHLIGHTED),
        label: 'home',
      ),
      BottomNavigationBarItem(
        icon: Image.asset(R.TAB_SOCIAL),
        activeIcon: Image.asset(R.TAB_SOCIAL_HIGHLIGHTED),
        label: 'community',
      ),
      BottomNavigationBarItem(
        icon: Image.asset(R.TAB_MY),
        activeIcon: Image.asset(R.TAB_MY_HIGHLIGHTED),
        label: 'I',)]; }}Copy the code

Page Route Configuration

class RoutePath {
  /// Configure home page - Contains (home/community/mine) for dynamically switching home pages
  static const zyMainPage = '/zyMainPage';

  /// details
  static const zyHomeDetailPage = '/zyHomeDetailPage';
}
Copy the code

Configure routes in main.dart

void main() {
  /// Common page route registration
  ZyAppRouter.register();

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo'./// The initialization route page is a separate page with the advantage of dynamically switching the home page
      initialRoute: RoutePath.zyMainPage,

      /// Routing hooksonGenerateRoute: ZyRoute.generator(), home: Container( color: Colors.white, ), ); }}Copy the code

The parameters and callback parameters are transmitted through routes

////////////////////The page is displayed with ///////////////////////
final result = await ZyRoute.pushNamed(
  context,
  RoutePath.zyHomeDetailPage,
  arguments: {
    'content': 'How are you, brother?',});String resultStr = result as String;
if(resultStr ! =null && resultStr.length > 0) {
  setState(() {
    receiveResult = resultStr;
  });
}

////////////////////Page callback parameter / / / / / / / / / / / / / / / / / / / / / / / / / /
ZyRoute.pop(context, 'You too!! ');
Copy the code

internationalization

When developing an App, if you need to support multiple languages, such as Chinese, English, traditional Chinese, etc., then we need to support internationalization.

Generate the ARB file

Now we can use the intl_translation package tool to extract the string from the code into an arB file and run the following name:

flutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart
Copy the code

Generate dart code

Generate dart file from ARB:

flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb
Copy the code

We can put the last two steps in a shell script and just execute the scripts separately when we have completed the third step or the arB file translation. We create an intl.sh script in the root directory

flutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-arb lib/l10n/localization_intl.dart
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb
Copy the code

Grant execution permission

chmod +x intl.sh
Copy the code

Perform the intl. Sh

./intl.sh
Copy the code

References:

  • Internationalization of Flutter

  • Flutter Combat – Internationalized – using Intl pack

Subject switch

Theme switching is already a must-have feature to make your App look better, but it can be tricky to fit different themes on traditional Android and iOS. But all of this is very easy to do in Flutter. Today we’re going to look at how to add skin peels to your App with Flutter.

We will use the Provider and SharedPreferencesUtils to complete the theme switching function.

  1. Use the Provider to manage global status
class ThemeViewModel extends ChangeNotifier {
  static const xThemeColorIndex = 'xThemeColorIndex';
  static const xThemeUserDarkMode = 'xThemeUserDarkMode';
  static const xFontIndex = 'xFontIndex';

  static const fontValueList = ['system'.'kuaile'];

  /// Light/shade mode selected by the user
  bool _userDarkMode;

  /// Current Theme Color
  MaterialColor _themeColor;

  /// Current font index
  int _fontIndex;

  ThemeViewModel() {
    /// Light/shade mode selected by the user
    _userDarkMode = SharedPreferencesUtils.getBool(xThemeUserDarkMode) ?? false;

    /// Get theme color
    _themeColor = Colors.primaries[
    SharedPreferencesUtils.getInt(xThemeColorIndex) ?? 5];

    /// To get the font
    _fontIndex = SharedPreferencesUtils.getInt(xFontIndex) ?? 0;
  }

  int get fontIndex => _fontIndex;

  /// Toggle specified colors
  void switchTheme({bool userDarkMode, MaterialColor color}) {
    _userDarkMode = userDarkMode ?? _userDarkMode;
    _themeColor = color ?? _themeColor;
    notifyListeners();
    saveTheme2Storage(_userDarkMode, _themeColor);
  }

  /// Switch fonts
  switchFont(int index) {
    _fontIndex = index;
    switchTheme();
    saveFontIndex(_fontIndex);
  }

  ThemeData themeData({bool platformDarkMode: false{})var isDark = false/*platformDarkMode || _userDarkMode*/;
    Brightness brightness = Brightness.light/*isDark ? Brightness.dark : Brightness.light*/;

    var themeColor = _themeColor;
    var accentColor = isDark ? themeColor[700] : _themeColor;
    var themeData = ThemeData(
        brightness: brightness,
        primaryColorBrightness: Brightness.dark,
        accentColorBrightness: Brightness.dark,
        primarySwatch: themeColor,
        accentColor: accentColor,
        fontFamily: fontValueList[fontIndex]);

    themeData = themeData.copyWith(
      brightness: brightness,
      accentColor: accentColor,
      cupertinoOverrideTheme: CupertinoThemeData(
        primaryColor: themeColor,
        brightness: brightness,
      ),

      appBarTheme: themeData.appBarTheme.copyWith(color: Colors.blue, elevation: 0),
      splashColor: themeColor.withAlpha(50),
      hintColor: themeData.hintColor.withAlpha(90),
      errorColor: Colors.red,
      cursorColor: accentColor,
      textTheme: themeData.textTheme.copyWith(
        /// To solve problems in Chinese don't hint in https://github.com/flutter/flutter/issues/40248
          subhead: themeData.textTheme.subhead
              .copyWith(textBaseline: TextBaseline.alphabetic)),
      textSelectionColor: accentColor.withAlpha(60),
      textSelectionHandleColor: accentColor.withAlpha(60),
      toggleableActiveColor: accentColor,
      chipTheme: themeData.chipTheme.copyWith(
        pressElevation: 0,
        padding: EdgeInsets.symmetric(horizontal: 10),
        labelStyle: themeData.textTheme.caption,
        backgroundColor: themeData.chipTheme.backgroundColor.withOpacity(0.1),),// textTheme: CupertinoTextThemeData(brightness: Brightness.light)
      inputDecorationTheme: ThemeHelper.inputDecorationTheme(themeData),
    );
    return themeData;
  }

  /// Data is persisted to shared Preferences
  saveTheme2Storage(bool userDarkMode, MaterialColor themeColor) async{
    var index = Colors.primaries.indexOf(themeColor);
    await Future.wait([
      SharedPreferencesUtils.setBool(userDarkMode, xThemeUserDarkMode),
      SharedPreferencesUtils.setInt(index, xThemeColorIndex)
    ]);
  }

  static String fontName(index, context) {
    switch(index) {
      case 0:
        return '0';
      case 1:
        return '1';
      default:
        return ' '; }}/// Font selection persistence
  saveFontIndex(int index) async{
    awaitSharedPreferencesUtils.setInt(index, xFontIndex); }}Copy the code
  1. Dart: Use MultiProvider if you have multiple state managers, and use Provider. Value if you have a single state manager. (I’m going to go straight to MultiProvider for future projects)
class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    returnMultiProvider( providers: [ ChangeNotifierProvider<ThemeViewModel>( create: (context) => ThemeViewModel(), ) ], child: Consumer<ThemeViewModel>( builder: (_, themeViewModel, __) { ThemeData themeData = themeViewModel? .themeData();return MaterialApp(
            theme: themeData,
            builder: BotToastInit(),
            navigatorObservers: [BotToastNavigatorObserver()],
            darkTheme: themeViewModel.themeData(platformDarkMode: true), onGenerateRoute: BaseRouter.generateRoute, initialRoute: AppRouteName.mainPage, ); },),); }}Copy the code
  1. Switching theme colors
var model = Provider.of<ThemeViewModel>(context,listen: false);
// var brightness = Theme.of(context).brightness;
model.switchTheme(color: color);
Copy the code

References:

Flutter Theme switch – allows your APP to skin change with one click

Third, summary

Although our partners have had a few years of experience in the field of Flutter, we are still in the early stages of building the system and there is still a lot of work to be done. We are moving towards building Flutter into a unified framework for basic research and development for mobile applications by combing through existing component libraries, integrating technologies, Team discussion, determine a set of standards, skilled use, so that business students pay more attention to the realization of business, so as to improve the development efficiency.

Share this next time

  • Event bus
  • Storage management
  • State management
  • network

reference

  • Flutter of actual combat

Author’s brief introduction

Sasuke, Flutter engineer, from the Smart Cloud Health Mobile Infrastructure team

At the end

Thank you for reading, a few days ago, zhiyun health front end team is participating in nuggets popular team selection activities. If you think it’s ok, then come and vote for us!

There are 15 votes available today, 5 votes for web pages, 5 votes for apps, and 5 votes for sharing. Thanks for your support, we will be writing more technical articles in 2021

Your support is our biggest motivation ~