1. Destroy and uncreated calls
Stop or destroy a listener in a timely manner, such as a timer:
Timer _countdownTimer; @override void dispose() { _countdownTimer? .cancel(); _countdownTimer = null; super.dispose(); }Copy the code
To be sure, we also need to check if the current page exists before calling setState() :
_countdownTimer = Timer.periodic(Duration(seconds: 2), (timer) { if (mounted){ setState(() { }); }});Copy the code
2. Draw first and request later
The addPostFrameCallback callback is triggered when the Widget is rendered, so it is used to get the size and position of the Widget on the page.
The solution is to use the addPostFrameCallback callback method and wait for the page build to complete before requesting data:
@ override void initState () {WidgetsBinding. Instance. AddPostFrameCallback ((_) {/ / / interface request}); }Copy the code
3. Keep the page status
For example, clicking the navigation bar to switch back and forth between pages will lose the original page state by default, that is, the page will be reinitialized with each switch. This solution is PageView combined with BottomNavigationBar, child pages at the same time the State of inheritance AutomaticKeepAliveClientMixin rewrite wantKeepAlive to true. The code looks like this:
class _TestState extends State<Test> with AutomaticKeepAliveClientMixin{
@override
Widget build(BuildContext context) {
super.build(context);
return Container();
}
@override
bool get wantKeepAlive => true;
}
Copy the code
4. Pre-cache images
There is a loading process for loading local images in Flutter. For example, when clicking the icon to switch the icon, it will flash for the first time. In particular, when you swipe left or right to switch images, there will be an obvious white screen flash.
The solution is simple to use precacheImage, which prestores the image into the image cache. If the Image is later used by Image, BoxDecation, or FadeInImage, it will load faster.
precacheImage(AssetImage("assets/logo"), context);
Copy the code
See the code for details of this problem: Click to view.
5. Screen orientation
New Flutter projects do not restrict vertical or horizontal screens by default, so if your project does not fit vertical or horizontal screens, you will need to restrict one direction. I’ll take limiting portrait as an example:
void main(){
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown
]).then((_){
runApp(MyApp());
});
}
Copy the code
6. FutureBuilder lazy loading
Flutter can be easily loaded lazily with FutureBuilder or StreamBuilder. Data can be “asynchronously” retrieved from future or stream, and then loaded with data from AsyncSnapshot. As for the concepts of flow and asynchrony, I’ll expand on that later.
Const FutureBuilder({Key Key, this.future,// method to get data this.InitialData, @required this.builder// Return different widgets based on snapshot state}) : assert(builder ! = null), super(key: key);Copy the code
Future is a defined asynchronous operation. If the snapshot is dynamic, the snapshot will be a snapshot of the state of the asynchronous operation. There are four snapshot states to load different widgets based on their connectionState:
Enum ConnectionState {// Future Not yet executed snapshot state None, // Connect to an asynchronous operation and wait for the interaction, in this state we can load a chrysanthemum waiting, // connect to an active operation such as stream, // The result of the asynchronous operation is done, and the corresponding layout is displayed.}Copy the code
StreamBuilder Flow Control Management
Emit an event from one end and listen for the change of the event from the other end. Through Stream, we can design responsive code logic on Flutter based on event flow. It is used in asynchronous task scenarios where data is read multiple times, such as network content download and file read and write.
class StreamDemo extends StatefulWidget { @override _StreamDemoState createState() => _StreamDemoState(); } class _StreamDemoState extends State<StreamDemo> { var index = 0; var streamController; StreamSink<String> get streamSink => streamController.sink; Stream<String> get streamData => streamController.stream; @override void initState() { super.initState(); streamController = StreamController<String>(); streamSink.add("0"); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('streamBuilder')), body: StreamBuilder<String>( stream: streamData, builder: (BuildContext context, AsyncSnapshot<String> snapshot) { return Text('Result: ${snapshot.data}'); }, ), floatingActionButton: FloatingActionButton( onPressed: onFloatActionButtonPress, child: Icon(Icons.add))); } void onFloatActionButtonPress() { index++; streamSink.add(index.toString()); }}Copy the code
8. How the Future initiates multiple asynchronous operations
For complex, time-consuming operations or multiple network requests with high integrity requirements, the Future asynchronous method can be used:
Future.wait([// return result future. delayed(new Duration(seconds: 2), () {return "hello";}), // future.delayed (new Duration(seconds: 4), () { return " world"; }) ]).then((results){ print(results[0]+results[1]); }).catchError((e){ print(e); });Copy the code
9. Adjust the upper and lower margins of Text
The colored rectangle on the left is the pillar (although the pillar actually has no width). The height of this rectangle is the minimum line height. This line can’t be any shorter. But it could be higher.
- The rise is the distance from the baseline to the top of the text (defined by the font, not any particular glyph)
- Drop is the distance from baseline to the bottom of the text (defined by the font, not any particular glyph)
A leading (pronounced “ledding”, as in the type metal used in old typesetters) is the distance between the bottom of a line and the top of the next line. In struts, half of the leads are placed at the top and half at the bottom. These are the gray areas in the graph. You can use multipliers to change the vertical dimensions of the struts.
class TextLineHeight extends StatelessWidget { final String textContent; final double leading; final double textLineHeight; final double fontSize; TextLineHeight({this.textContent, this.leading: 0, this.textlineheight: 0.93, this.fontsize: 26,}); @override Widget build(BuildContext context) {return transform. translate(offset: offset (0, fontSize/11.52), child: Text( textContent, strutStyle: StrutStyle( forceStrutHeight: true, height: textLineHeight, leading: leading, fontSize: fontSize, ), style: TextStyle( fontSize: fontSize, color: Colors.black, ), ), ); }}Copy the code
Effect comparison:
10. Empty judgment
Use null-Aware operators to determine null and reduce code.
// User below title ?? = "Title"; // instead of if (title == null) { title = "Title"; }Copy the code
The above method can be used to protect only one level of judgment, which is needed if you have a series of ‘. ‘values.
xxCount = xxModel? .xxInfo? .xxCount ?? 0Copy the code
11. VSCode configuration items
{" version ":" 0.2.0, "" configurations: [{" name" : "Main", "type" : "dart", "request" : "launch", "the program" : }, {"name": "Dev", "type": "dart", "request": "launch", "program": "lib/main_dev.dart", "args": [ "--flavor", "universal", ], "flutterMode": }, {"name": "Prod", "type": "dart", "request": "launch", "program": "lib/main_prod.dart", "args": [ "--flavor", "universal", ], }, ] }Copy the code
12. Use the private library Pubspec.yaml
Git private libraries can be specified to reference links, branches, and tags.
Dependencies: # routing mixed library flutter_boost: git: url: [email protected]: ReverseScale/flutter_boost git ref: v1.1Copy the code
Liverpoolfc.tv: dart. Dev/tools/pub/d…
13. Check whether the release environment is used
Determine whether the current environment is in release mode:
const bool kReleaseMode = bool.fromEnvironment('dart.vm.product')
Copy the code
Use the system-level top-level state kReleaseMode to get the current release environment:
import 'package:flutter/foundation.dart';
print('Is Release Mode: $kReleaseMode');
Copy the code
This can be used to control logging output, such as turning off logging in Release mode:
if (isProduction) {
debugPrint = (String message, {int wrapWidth}) => {};
}
Copy the code
14. Status bar height
final double statusBarHeight = MediaQuery.of(context).padding.top;
Copy the code
15. Height of the bottom bar
final double bottomHeight = MediaQuery.of(context).padding.bottom;
Copy the code
16. Scientific notation
Var number = 1 / pow(10, 8); 0.00000001 print(aaa.toStringasFixed (8));Copy the code
17.InheritedWidget stores value methods
1. Declare ShareDataWidget
class ShareDataWidget extends InheritedWidget { int count; ShareDataWidget({ @required this.count, Widget child }): super(child: child); static ShareDataWidget of(BuildContext context) { return context.inheritFromWidgetOfExactType(ShareDataWidget); } @override bool updateShouldNotify(InheritedWidget oldWidget) { return true; }}Copy the code
2. Store the pass value, for example: body nested pass value:
body: ShareDataWidget( count: this.widget.count, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment spaceAround, children: < widgets > [Text (" number of widgets: ${this.widget.count}"), _Counter(), RaisedButton(Child: Text(" added "), onPressed: () => setState(() => widget.count ++), ) ] ), ), )Copy the code
3. Fetch the transfer value:
ShareDataWidget sdw = context.inheritFromWidgetOfExactType(ShareDataWidget);
print(sdw.count);
Copy the code
18.Cocoapod introduces the Flutter Framework
flutter_application_path = '.. /Seller/'load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')install_all_flutter_pods(flutter_application_path)Copy the code
19. Mixins Syntax (Advanced)
Class Musician extends Person with Musical {// ···} Class Maestro extends Person with Musical, Aggressive, Demented { Maestro(String maestroName) { name = maestroName; canConduct = true; } } mixin Musical { bool canPlayPiano = false; bool canCompose = false; bool canConduct = false; void entertainMe() { if (canPlayPiano) { print('Playing piano'); } else if (canConduct) { print('Waving hands'); } else { print('Humming to self'); }}}Copy the code
20. Generic examples
var names = <String>['Seth', 'Kathy', 'Lars']; var pages = <String, String>{ 'index.html': 'Homepage', 'robots.txt': 'Hints for web robots', 'humans.txt': 'We are people, not machines' }; To specify one or more types when using a constructor, put the types in angle brackets (<... >) just after the class name. For example: var names = List<String>(); names.addAll(['Seth', 'Kathy', 'Lars']); var nameSet = Set<String>.from(names); var views = Map<int, View>();Copy the code
Print generic types
class Foo<T extends SomeBaseClass> { // Implementation goes here... String toString() => "Instance of 'Foo<$T>'"; } class Extender extends SomeBaseClass {... }Copy the code
21. The import libraries
Library conflict
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
Copy the code
Import part of the library
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
Copy the code
Lazy loading library
import 'package:greetings/hello.dart' deferred as hello;
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
Copy the code
22.Generators (Iterative Generators)
Synchronous Generators
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
Copy the code
Asynchronous Generators
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
Copy the code
Define metadata
The sample
library todo; class Todo { final String who; final String what; const Todo(this.who, this.what); } use import 'todo.dart'; @Todo('seth', 'make this do something') void doSomething() { print('do something'); }Copy the code
24. Private Pub
Address: pub. Dev/packages/pu…
~ $ git clone <https://github.com/dart-lang/pub_server.git>
~ $ cd pub_server
~/pub_server $ pub get
...
~/pub_server $ dart example/example.dart -d /tmp/package-db
Copy the code
Parameter Description:
-s Whether to fetch the official repository -h ${IP/domain} -p port -d Storage address of the uploaded plug-in package on the serverCopy the code
Using it it is easy to upload new packages to a server running locally or download locally available packages or by backing back to the following packages pub.dartlang.org:
~/foobar $ export PUB_HOSTED_URL=http://localhost:8080 ~/foobar $ pub get ... ~ / foobar $pub publish Publishing 0.1.0 from x to < http://localhost:8080 > : | -... '-- pubspec.yaml Looks great! Are you ready to upload your package (y/n)? y Uploading... Successfully uploaded package.Copy the code
Complete the pubspec.yaml file
Description: plugin description version: 0.0.1 author: XXXX <[email protected]> Homepage: publish_to: Enter the address of the private server (this is not required if you publish to the flutter pub, the plugin is uploaded to the flutter pub by default).Copy the code
Publish to a private server
Flutter Packages pub publish --server $Server addressCopy the code
25. There are limitations on the type of data interaction between native and Flutter
Data interaction between native and Flutter must be involved in the development of the plugin. It’s important to note that not all types of data support interaction, as we did with ReactNative and JNI. Here I give the data types that native and Flutter can interact with:
Data type comparison
The types we use most here are bool, int, String, Map.
26. Detection mode
Flutter already encapsulates the method of detecting patterns for us:
import 'package:flutter/foundation.dart'; If (kReleaseMode){// Release mode to do} if(kProfileMode){// profile mode to do} if(kDebugMode){// debug mode to do}Copy the code
Take a look at the source:
// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /// A constant that is true if the application was compiled in release mode. /// /// More specifically, this is a constant that is true if the application was /// compiled in Dart with the '-Ddart.vm.product=true' flag. /// /// Since this is a const value, it can be used to indicate to the compiler that /// a particular block of code will not be executed in release mode, and hence /// can be removed. const bool kReleaseMode = bool.fromEnvironment('dart.vm.product', defaultValue: false); /// A constant that is true if the application was compiled in profile mode. /// /// More specifically, this is a constant that is true if the application was /// compiled in Dart with the '-Ddart.vm.profile=true' flag. /// /// Since this is a const value, it can be used to indicate to the compiler that /// a particular block of code will not be executed in profle mode, an hence /// can be removed. const bool kProfileMode = bool.fromEnvironment('dart.vm.profile', defaultValue: false); /// A constant that is true if the application was compiled in debug mode. /// /// More specifically, this is a constant that is true if the application was /// not compiled with '-Ddart.vm.product=true' and '-Ddart.vm.profile=true'. /// /// Since this is a const value, it can be used to indicate to the compiler that /// a particular block of code will not be executed in debug mode, and hence /// can be removed. const bool kDebugMode = ! kReleaseMode && ! kProfileMode;Copy the code
Source code simply defined three constants: kReleaseMode, kProfileMode, kDebugMode corresponding to the release mode, profile mode, debug mode
The principle is a wrapper around bool.fromEnvironment(), but with less stuff and default values, it’s ok to use it.
27.Assert the usefulness of an assert
Assert runs only in Debug mode and can be used not only to determine what mode to run, but also to check that your program is running as required.
For example, a function cannot pass a null argument:
Void test(String name){assert((){print(' user name is null '); name! =null }()); // Other things}Copy the code
In the example above, name is the user name retrieved from the background, but if it is not retrieved or the background reports an error, you can quickly locate the error.
28.Three schemes for data transfer
- InheritedWidget: Applies to scenarios where a parent component passes to a child component, across hierarchies.
- Notification: Applies when a child component notifies its parent of data changes.
- Event Bus: Event broadcast, suitable for various types of data notification synchronization.
29. Bottleneck of live drawing
The Channel mechanism defined by Flutter essentially provides a message transfer mechanism for transferring data such as images, which inevitably causes a huge consumption of memory and CPU.
Solution: External Texture + PixelBuffer channel transfers OpenGLES Texture to Skia for direct drawing.
- ** Saves CPU time. ** From our test results, it takes about 5ms to read 720P RGBA video from THE GPU to the CPU, and another 5ms to transfer it from the CPU to the GPU. Even with the introduction of PBO, it still takes about 5ms. This is obviously unacceptable for high frame rate scenarios.
- ** Saves CPU memory. ** Data is passed on the GPU, especially for image scenarios where there may be many images to display at the same time.
30.Sentry exception collection
By default, the Flutter engine helps us collect unhandled exceptions and provides a unified interface for handling them. By registering call-back to FlutterError. OnError, these exceptions can be handled manually. In conjunction with Sentry, exceptions and their detailed stack information can be collected automatically.
Add the Dart version of the Sentry client and initialize it with the DSN, which can be found in the Sentry panel.
import 'package:sentry/sentry.dart';
final sentry = SentryClient(dsn: 'DSN');
Copy the code
Introduce a flutter/fondation and register a callback. The exception information will be uploaded via the Sentry client.
import 'package:flutter/foundation.dart'; . FlutterError.onError = (e) => sentry.captureException(exception: e.exception, stackTrace: e.stack); .Copy the code
However, in a development environment, we usually want to output exception messages directly to the console to help developers fix problems quickly, rather than feeding them back to Sentry. Dart. Product, the DartVM environment variable, allows us to determine if we are currently in production mode and to register exception collection callbacks only in production mode.
. if (bool.fromEnvironment('dart.vm.product')) FlutterError.onError = (e) => sentry.captureException(exception: e.exception, stackTrace: e.stack); .Copy the code
Dispose method Attention
Overriding the Dispose method and configuring the AnimationController instance.
@override
dispose() {
animationController.dispose();
super.dispose();
}
Copy the code
Get the Render method
Create GlobalKey
GlobalKey globalKey = GlobalKey();
Copy the code
Corresponding Widget reference, such as Text here
The Text (' zhang 'key: globalKey);Copy the code
Get the corresponding Element by globalKey (BuildContext)
BuildContext stackContext = globalKey.currentContext;
Copy the code
Get the corresponding RenderObject
RenderBox renderBox = stackContext.findRenderObject(); Renderbox.localtoglobal (offset.zero); / / / get the Size of the specified Widget information Size Size = renderBox. PaintBounds. Size;Copy the code