As the third part of this series, this article will focus on the packaging process of Flutter development, APP package comparison, detailed skills and problem solving. This article will mainly describe the packaging of Flutter and various problems and details encountered during the development process, which is a complement to the previous two articles.

Article summary address:

A complete series of articles on Flutter

A series of articles on the world outside Flutter

A, packaging,

First of all, we will look at the results, as shown in the table below, which are vertical and horizontal comparisons of Flutter and React Native, iOS and Android.

project IOS Android
GSYGithubAppFlutter
GSYGithubAppRN

As can be seen from the table above:

  • Fluuter’s APK is smaller than the IPA, in part because the Skia that Flutter uses comes with Android.

  • Horizontally compared with React Native, although the project is not exactly the same, the Apk of Flutter is indeed smaller when most functions are the same. Another detail here is that RN’s IPA package is much smaller because javascriptCore is built into ios.

  • For those interested in the above, check out the In-depth Analysis of Cross-platform development on mobile.

1. Android packaging

In Android /app/build.grade file, configure applicationId, versionCode, versionName and signature information. Finally, compile the flutter build app. Programming successful package in the build/app/outputs/apk/release.

2, iOS packaging and real machine running

On the packaging of iOS, the author has experienced a wave of twists and turns, and here is mainly about the problems that the author has encountered.

You’ll need an Apple developer account first, then create a certificate, create an AppId, create a profile, and finally enter the relevant information in the info.plist file. For more details, see the tutorial for the official Released IOS APP.

However, due to the use of third-party plug-in packages such as shared_preferences in the author’s project, the following problems have been encountered during the implementation of Archive:

Could not be found in 'Archive'#import 
      
        ///file not found
      
#import <device_info/DeviceInfoPlugin.h>
#import <flutter_statusbar/FlutterStatusbarPlugin.h>
#import <flutter_webview_plugin/FlutterWebviewPlugin.h>
#import <fluttertoast/FluttertoastPlugin.h>
#import <get_version/GetVersionPlugin.h>
#import <package_info/PackageInfoPlugin.h>
#import <share/SharePlugin.h>
#import <shared_preferences/SharedPreferencesPlugin.h>
#import <sqflite/SqflitePlugin.h>
#import <url_launcher/UrlLauncherPlugin.h>
Copy the code

Running the iOS emulator through Android Studio has no problems, indicating that this is not a third-party package issue. According to the problem finding, before iOS can execute Archive, the flutter build release needs to be executed, as shown in the following figure. After the command is executed, the execution directory of Pod will be found changed and the files needed for packaging will be generated. (Ps normal runtime will automatically change back)

Ak47 schematic item and loot added after implementing the build release of flutter, problems still exist. Finally, ak47 schematic item and loot added are found:

  • Similar problems are described under Issue#19241, but they cannot be solved after attempts due to path problems.

  • Issue#18305 actually solved this problem because the Pod project was not introduced:

open ios/Runner.xcodeproj

I checked Runner/Pods is empty in Xcode sidebar.

drop Pods/Pods.xcodeproj into Runner/Pods.

"Valid architectures" to only "arm64" (I removed armv7 armv7s) 
Copy the code

Finally successfully packed, heart tired ah (/// /▽///). Also, if you want to debug Flutter directly on a real machine, you can refer to the iOS Real machine section of Flutter Fundamentals – Development Environment and Getting Started.

Second, the details

Here are the minor details

1, AppBar

AppBar is a common Widget in Flutter. AppBar can be used not only as a title bar, but also as a leading and bottom function on Flutter.

  • The AppBarbottomThe default supportTabBarThat’s the usual top Tab effect, and this is actually becauseTabBarTo achieve thePreferredSizeWidgetpreferredSize. So as long as your control is implementedpreferredSize, you can put it in AppBarbottomThe use of. For example, in the search bar below, this is a TabView page with AppBar in it.

  • Leading: The left button. If this parameter is not set, the icon or return button of a Drawer is displayed.

  • FlexibleSpace: Between Bottom and leading.

2, buttons,

Keys in a Flutter, such as the FlatButton, have margins and minimum sizes by default. So if you want no padding, margin, border, default size, etc., one way to do this is as follows:

/// new RawMaterialButton( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, padding: padding ?? Const EdgeInsets. All (0.0), constraints: const BoxConstraints(minWidth: 0.0, minHeight: 0.0), child: child, onPressed: onPressed);Copy the code

If you add Flex, as shown below, a controlled fill button will appear.

new RawMaterialButton( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, padding: padding ?? Const EdgeInsets. All (0.0), constraints: const BoxConstraints(minWidth: 0.0, minHeight: 0.0), /// Flex child: new Flex( mainAxisAlignment: mainAxisAlignment, direction: Axis.horizontal, children: <Widget>[], ), onPressed: onPressed);Copy the code

3. StatefulWidget assignment

Here we take active assignment to a TextField as an example. In a Flutter, state or data is transferred to a stateful Widget via various controllers. As in TextField’s active assignment, the code looks like this:

 final TextEditingController controller = new TextEditingController();

 @override
 void didChangeDependencies() { super.didChangeDependencies(); Controller. Value = new TextEditingValue(text: TextEditingValue)"Fill in the input field with parameters.");
 }

 @override
  Widget build(BuildContext context) {
    return new TextField(
     ///controller
      controller: controller,
      onChanged: onChanged,
      obscureText: obscureText,
      decoration: new InputDecoration(
        hintText: hintText,
        icon: iconData == null ? null : new Icon(iconData),
      ),
    );
  }
Copy the code

TextEditingValue is ValueNotifier, where the setter method for value is overridden and changes to it trigger notifyListeners. In TextEditingController, you’re listening for data changes by calling addListener to update the UI.

Of course, assignment is simpler and more crude: pass an object class A, bind the control with the variable of A.b inside the control, and update it externally with setState({A.b = b2}).

4, GlobalKey

To actively change the state of child controls in Flutter, you can also use GlobalKey. For example, you need to actively call the RefreshIndicator to show the refresh status, as shown in the code below.

 GlobalKey<RefreshIndicatorState> refreshIndicatorKey;
  
 showForRefresh() {/ / / display refresh refreshIndicatorKey. CurrentState. The show (); } @override Widget build(BuildContext context) { refreshIndicatorKey = new GlobalKey<RefreshIndicatorState>();returnNew RefreshIndicator(Key: refreshIndicatorKey, onRefresh: onRefresh, Child: New ListView.Builder (///·····),); }Copy the code

5. Redux and themes

Redux is the best way to manage the global State of Flutter. If you are interested in Redux, you can refer to chapter 2, where Redux is used to change the theme in real time.

Store is loaded via StoreProvider, and themeData in Store is bound to the theme of the MaterialApp via StoreBuilder. You can then use Theme. Of (context) to change the color you want in any other Widget, and finally call Store. dispatch from any location to change the Theme in real time, as shown in the following image.

class FlutterReduxApp extends StatelessWidget { final store = new Store<GSYState>( appReducer, initialState: new GSYState( themeData: new ThemeData( primarySwatch: GSYColors.primarySwatch, ), ), ); FlutterReduxApp({Key key}) : super(key: key); @override Widget Build (BuildContext Context) {/// Through StoreProvider app StorereturnNew StoreProvider(store: store, /// Obtain themeData Child: new StoreBuilder<GSYState>(Builder: (context, store) {return new MaterialApp(
            theme: store.state.themeData,
            routes: {
              HomePage.sName: (context) {
                returnHomePage(); }}); })); }}Copy the code

6. Hotload and Package

Flutter is in JIT mode under Debug and AOT mode under Release. Flutter supports Hotload under Debug and is very smooth. However, it is important to note that if you install a new third-party package during development and the new third-party package contains native code, you need to stop and run it again.

Yaml file is our package dependency directory, where ^ stands for greater than or equal to. In general, upgrade and get can achieve the function of downloading packages. However: upgrade will update the version of the package under the pubspec.lock file if the package has been updated.

Third, problem handling

  • Waiting for another flutter command to release the startup lock: If you encounter this problem:
Open the /bin/cache/ installation directory of flutter. Delete the lockfile file. Restart AndroidStudioCopy the code
  • Yellow-lines-under-text-widgets-in– showDialog the Scaffold was not used by default. This Scaffold could be processed using the Material package layer 1.

  • TabBar + TabView + KeepAlive = TabBar + PageView

Since then, the third chapter is finally over! (/ / / del / / /)

Resources to recommend

  • Making: github.com/CarGuo/
  • Open Source Flutter complete project:Github.com/CarGuo/GSYG…
  • Open Source Flutter Multi-case learning project:Github.com/CarGuo/GSYF…
  • Open Source Fluttre Combat Ebook Project:Github.com/CarGuo/GSYF…

Full open source project recommendation:

  • GSYGithubAppWeex
  • GSYGithubApp React Native