1. Do we need Flutter?

Unlike RN, Flutter doesn’t have anything to do with native iOS/Android components like UIButton. All the elements of Flutter are drawn by itself, while RN just makes a bridge call. Understand that this can be very useful for you to make decisions/fool your boss

To start, we assume that you already have some basic concepts of Flutter, such as Release/Debug versions, such as how to run a HelloWord

2. Mixed stack:

Dad’s guide here

Google makes it very clear that you can mix the two by looking for 1234567. Of course you can also Google flutter mix.

Here are a few things that dads don’t tell you about the configuration:

0.flutter create -t module xxxThe -t

There are many instances where the flutter create does not have -t. Simply put, without -t, your iOS/ Android projects will be submitted with Git and they will never be submitted. Anyone who pulls new code needs to flutter Create to run properly.

You don’t need to change any native works written with Flutter, and your iOS folder won’t be affected by SDK changes or Xcode changes, but please be honest with society:

  • Default generated iOS folderpodfileThere is no! use_framworkthe
  • Are you sure you don’t need to change plist to add whitelist or something
  • Are you sure you don’t need some configuration in the AppDelegate to assist your third-party libraries like wechat

So if -t, you as the architect/researcher should be prepared to write scripts to modify these files.

Also please run flutter Packages get properly, because it will rearrange the contents of your iOS folder, and the flutter build –release will help you get flutter packages by default, so if you have your own initialization script, The order of execution should be:

  • flutter packages get
  • Your script
  • flutter build --release -no-pub -no-pubWill be ignoredbuildAt the time of theget

1. Build PhasesIn thescriptYou don’t always need to run

Flutter Release

  • To put it bluntly, if you just debug the script without running it, the results are the sameFlutter Debugmodel
  • This script increases compilation time, so no needflutterThe update engineer doesn’t need to care if he runs away
  • If you areDebugThe main project wants to runReleasetheFlutterIf you don’t run this Script will crash, that’s whyFlutter ReleaseConfiguration requires this script to complete

2. Be honest and follow Google’s recommendations. Your goal is to run and quickly get familiar with dart andflutterLayout is not a study of native withflutterAnd frame coupling

3. In the package scriptFlutter runIs not reasonable

You may also use Jenkins or Fastlane to pack QA. If you want to generate products, go to the build –release command of Flutter because Flutter run will directly freeze the process and you will not be able to continue. Although you can choose to run in the background, But you can’t guarantee synchronization

4. Jump or walkflutter_boost

As for why, you can turn over the article written by salted fish, or from the simplified that is:

  • In Google’s stack of Flutter blends,FlutterViewControllerIn the bellyFlutter engineSo if you have a pure Flutter project you will notice that there is only one VC, and he draws new pages in a single VC to complete the push operation
  • But in the mix stack you definitely willFlutterViewControllerPush the next oneFlutterViewController“, which will cause your application to crash in a matter of time
  • The salted fish solution is to keep one engine, then take the engine singleton from the previous VC to the next while pushing, and then reproduce the slide back or pop via screenshots and so on

Of course, if you are the kind of very good must play by yourself, that is also ok, otherwise see 2 said in here also applies ~

Query: {“query”: {“name” : XXX}} {“query”: {“name” : XXX}}} {“query”: {“name” : XXX}}} {“query”: {“name” : XXX}}}} {“query”: {“name” : XXX}}

5. MethodChannel series interactions

For normal interactive registration, you can click here. It’s very simple

The FlutterPlugin is an elegant pose, though it’s officially recommended

I am an elegant 🌰

To be clear, MethodChannel just needs to have a chance to register with the Flutter engine at any time, so we found the time for FlutterPlugin. All third-party flutter plugin (which most is part of a native calling through the channel to solve), actually also is injected into the engine by means of registered, if you want to see how come, you can find in your project GeneratedPluginRegistrant. M this file, This is a system-generated file that will help you register all the plug-ins you have introduced into Flutter:

@implementation GeneratedPluginRegistrant + (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry { [FLTDeviceInfoPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTDeviceInfoPlugin"]]; . } @endCopy the code

So we had a bold idea. If we could build a plugin of our own, with a channel in it, that would allow all the flutter to interact with our existing main project, wouldn’t it be elegant? But the problem is that: the system generated GeneratedPluginRegistrant how do I add my own plug-in inside? Because my own mods are in the main project, not introduced in Flutter…

By this time you might have thought of the dark magic that was forgotten during the Swift era:

- (instancetype)init
{
    if (self = [super init]) {
        _viewController = [FLBFlutterViewControllerAdaptor new];
        [_viewController view];
        Class clazz = NSClassFromString(@"GeneratedPluginRegistrant");
        if (clazz) {
            if ([clazz respondsToSelector:NSSelectorFromString(@"registerWithRegistry:")]) {
                [clazz performSelector:NSSelectorFromString(@"registerWithRegistry:")
                            withObject:_viewController];
            }
        }
    }
    
    return self;
}
Copy the code

The above is to find the flutter_boost, so you should know that in fact you can also to steal the registerWithRegistry GeneratedPluginRegistrant method to register first flutter own plugin. Re-registering the main project plugin has maintained a graceful posture ~

6. What should I communicate with native

The FlutterResult class specifies that a String should be passed, so please repeat this sentence. Do not make a data into a String and then pass it to flutter to change it back to data or even image. Remember, You can’t read the image string, and neither can Flutter. Json strings can be read by you, so can flutter

So for complex files, just know that Flutter can’t read data, but it can also access NSTemporaryDirectory, so you can support both parties to share the same data with a temporary folder path. This is not recommended, but it seems to be the best you can do

private func saveToFile(image: UIImage) -> String? { guard let data = image.jpg(compressionQuality: Else {return nil} let tempDir = NSTemporaryDirectory() let imageName = "image_picker_\(ProcessInfo().globallyUniqueString).jpg" let filePath = tempDir.appending(imageName) if FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil) { return filePath } else { return nil } }Copy the code

Like this upstairs

3. DartWhat do I need to prepare

1. Abstract the underlying services

If the initial planning is to make the underlying services native, such as request login to take photos and so on, then ideally you can actually feel that the flutter code is all you need to do with the business.

Well, think back to you in RN who coded for 5 minutes and logged for 5 hours? So if you have time, please build all the infrastructure you can for Flutter to speed up debugging dramatically:

The following pattern can be used to maximize code extensibility

We’re through

God mode: isSonMode= false indicates the mode of flutter to run by itself

Son mode: isSonMode= true which is run mode in main project

This distinction is used to determine which underlying service we need to invoke

For example here is our DART side request call:

class RequestService { static RequestService shared = RequestService(); factory RequestService() => GlobalConfig.isSonMode ? SonRequestService() : GodRequestService(); Future<Map> Post (String URL, Map para) async {throw UnimplementedError("saveElement method is not implemented "); } Future<UploadItem> upload(UploadItem file) async {throw UnimplementedError("saveElement method is not implemented "); } } class GodRequestService implements RequestService { Future<Map> post(String url, Map para) async {// You can call dio or HTTP request here} Future<UploadItem> upload(UploadItem file) async {... }}Copy the code

We use globalConfig. isSonMode to switch between specific RequestService implementations to isolate each RequestService from contamination.

There are, of course, fancier judgments:

static Router _route =
      GlobalConfig.isAndroid ? AndroidRouteBox() : GlobalConfig.isSonMode ? FlutterBoostRouteBox() : GodRouteBox();
Copy the code

For example, for routing, flutter jump is used in the mode of Flutter God, flutter_boost is used in the iOS main project, and flutter jump is still used in the android main project

And you only need to do in the dart business code RequestService. Shared. Post… Can be normal request, do not need to care about the problem of switching.

(Of course, request abstraction will be covered in the next installment, so stay tuned.)

2. Model.fromJSON

Tools are everywhere and a little bit recommended for this one, which of course is extremely complimentary to handwriting perseverance ~ but note:

  • dartCompared with theSwiftIs more strict about the type
  • int/doubleIf the definition is reversed, it will explode1.0To pass tointType attribute, inSwiftIn no waves, indartIn the middle of the waves,dartA medium point can be usednumTo modify all the numbers
  • StringBoth types are recommendedtoString()

Plus an elegant one from Swift

var isUser : Dart: Bool {return XXX == 1 &&xxxx = 2} Bool get isUser => XXX == 1 && XXXX = 2 or bool get isUser {return XXX == 1 && XXXX = 2}Copy the code

This will reduce the amount of business computation in your Widget; let the Model do the defining

Forget about the page life cycle

Because the engine is drawn differently, the page life cycle of flutter does not have the right callbacks/proxies to trigger it. Don’t think of making strange requests to refresh in didUpdateWidget or Dependency. That’s not a place for you to do that. The only thing you can do seems to be do everything in initState;

This is a bit virtual, so all actions need to be triggered strictly from actions, not from the page level,

For example

Swift: Button Click -> Jump -> Back -> ViewWillAppear to refresh the whole page

Flutter: button Click -> refresh the corresponding Widget -> jump

In fact examples of Swift, also is not good, but actually we do it because of lazy basic, but we can’t know the page life cycle in the dart, so please use the right time to do the right thing, of course this would tolerate the screen full of setState, will surely someone told you that it is not reasonable ~ how to reasonable, here aside, After all, the goal here is to get the app running and online, as for elegant or not elegant behind the familiar will naturally have a feeling.

4. Resource pictures

In pubspec.yaml this will do

Assets: -assets /images/ -assets /images/2.0x/ -assets /images/3.0x/Copy the code

Sometimes you will find that you add a new picture and the result is put in the right position, but the result does not come out. It is ok that you debug it several times and it will come out. If you confirm that there is no problem with the name, it may not be displayed in time sometimes

5. The font

The interesting thing is that for the main entry:

Return MaterialApp(Title: 'I'm a demo', theme: ThemeData(fontFamily: platform.isios? 'PingFang SC' : null,) }Copy the code

You need to do this to make your font look good on iOS phones, otherwise it will look all kinds of weird, but there is a hidden pit:

The top widget on all your pages has to be in the Material family for this to work, these are the Material family,

So if your page says:

class DemoPage extends StatelessWidget { @override Widget build(BuildContext context) { return Container( child: Page elements); }}Copy the code

You’ll notice that your fonts are still weird, even though the page looks fine

Is not very simple, see here, pit certainly still have, but at least the big problem should be gone, you should be able to run your application happily ~