Flutter is an efficient cross-platform UI framework. Compared to React Native and WEEX, flutter does not need to be converted to native controls and does not need to be bridged by JScore as a middle layer. It can render UI directly through the SKIA engine, which has many advantages
This article is about flutter and responsive frameworks from the perspective of a front-end developer
Responsive frame
This is the basic structure of an MVVM framework
Basically, the MVVM framework is powerful for any scenario that requires rendering the UI
Because essentially it completely decouples the data from the view
mvc/p
Going back to the front-end MVC/P framework, these are some pretty pure data/view separation frameworks
It is highly modular, but not solvableSynchronization between data and viewsThe problem of
Or that the synchronization should be left to the platform (browser)
It wasn’t until front-end engineering became popular that various MVVM frameworks emerged
Their general principles are as follows:
This is what happens in Flutter:
Component nature
Ok, back to Flutter, this is Hello World in Flutter
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Welcome to Flutter'),
),
body: new Center(
child: new Text('Hello World'),),),); }}Copy the code
Most of the stuff in a flutter is a widget. To put it simply, it can be a front-end Component. It can exist either individually or nested as a parent Component.
Back in the days of jQuery, when jQuery plugins were very popular, you know, date plugins, pagination plugins and so on and each of these plugins could output a DOM that did a particular thing so is it a component? Yes.
Back in the present day, we also write various Component/ widgets to implement various functions
In layman’s terms, components are “functions that generate reusable UIs” except that they have different outputs — one for the real DOM and one for the virtual VNode
In MVVM, the VIEWMODEL automatically synchronizes the view with the data, and VNode is a big part of that in terms of minimizing changes to the view, and also enabling the framework to be cross-platform and so forth throughout the life cycle of the component
cross-platform
Based on VNode, many MVVM frameworks have cross-platform capabilities because they just need to generate VNode code for the corresponding platform. Weex and Rn do the same
Flutter, however, performs UI rendering directly through the SKIA engine. The advantage of Flutter is that it does not need jScore as an intermediate layer to bridge, resulting in less difference in UI performance between platforms and a significant performance improvement
Flutter for Web re-implements the DART: UI library, replacing the Skia engine binding used on mobile with code for DOM and Canvas.Copy the code
Component types
In front-end development, we will use both normal components and functional components. Normal components have their own state and life cycle, while functional components simply output the specified VNode and cannot change their state in flutter, especially
statelessWidget
Let’s look at a simple Flutter demo
class CounterDisplay extends StatelessWidget {
CounterDisplay({this.count});
final int count;
@override
Widget build(BuildContext context) {
return new Text('Count: $count'); }}class CounterIncrementor extends StatelessWidget {
CounterIncrementor({this.onPressed});
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return new RaisedButton(
onPressed: onPressed,
child: new Text('Increment')); }}Copy the code
Flutter encourages the use of stateless components. First, for performance reasons, functional components have obvious advantages in generation and diff. Second, more importantly, we need to get into the habit of looking at our code
stateFullWidget
Let’s take a look at this demo
class Counter extends StatefulWidget {
@override
_CounterState createState() => new _CounterState();
}
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
++_counter;
});
}
@override
Widget build(BuildContext context) {
return new Row(children: <Widget>[
new CounterIncrementor(onPressed: _increment),
newCounterDisplay(count: _counter), ]); }}Copy the code
React :State,setState,build, react
We should always remember that a Widget is a temporary object that may be called multiple times to build applications in its current State. It is only a configuration. However, the State object should not change between multiple calls to Build (). Allow them to remember states
A call to setState tells the Flutter framework that a state change has caused the application to rerun the Build method so that the application can reflect the change. Build is the equivalent of the JXS or Vue template returned by React.
Now that you understand this, you can move on to the interaction between components
State management
All communication between MVVM framework components follows this logic:
A parent class passes a value to a child class through an attribute and a child class passes a value to its parent class through an eventCopy the code
This is the clearest direction of the data flow. We can clearly see how the data changes, and this is certainly the case in flutter:
class Parent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
returnParentState(); }}class ParentState extends State<Parent> {
String data = "No";
Sring props = "some thing";
void onChanged(val){
setState(() {
data = val;
});
}
@override
Widget build(BuildContext context) {
return new Center(
child: new Column(
children: <Widget>[
new Container(
child: new Column(
children: <Widget>[
new Child(props: props,callBack: (value)=>onChanged(value)),
new Text('from child : $data'[[() [[() [() [() [() [() }}Copy the code
But in reality, business is never that simple, a lot of data needs to be shared and in MVVM it’s a case of a data change, associated views need to be reconstructed which is a natural fit for the observer model Redux, Vuex and all of these state management tools are based on that and to some extent EventBus, I won’t repeat it here
InheritedWidget
InheritedWidget is an important functional component of Flutter that provides a way for data to be passed and shared from top to bottom in the widget tree
In essence, it adds the current Widget as a dependency, notificating all dependencies to update it when its data changes. Using it is cumbersome. Let’s look at a demo of the ChangeNotifierProvider based on the inheritedWidget implementation
runApp(
// Provide the model to all widgets within the app. We're using
// ChangeNotifierProvider because that's a simple way to rebuild
// widgets when a model changes. We could also just use
// Provider, but then we would have to listen to Counter ourselves.
//
// Read Provider's docs to learn about all the available providers.
ChangeNotifierProvider(
// Initialize the model in the builder. That way, Provider
// can own Counter's lifecycle, making sure to call `dispose`
// when not needed anymore.
create: (context) => Counter(),
child: MyApp(),
),
);
}
class Counter with ChangeNotifier {
int value = 0;
void increment() {
value += 1; notifyListeners(); }}return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo Home Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Use data
Consumer(
builder: (context, counter, child) => Text(
'${counter.value}',
style: Theme.of(context).textTheme.display1,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
// Operate data
onPressed: () =>
Provider.of(context, listen: false).increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
Copy the code
We use the ChangeNotifierProvider to register the observed, use the Consumer to register the observer provider.of (context) method to get the observed, process the data logic and notify the observer to rerender
Of course, you can do this without the hassle of using a direct reference if you really know what you’re doing and only use it in a specific context (if you’re using it in a normal component, take a look at the code, it can mess up the flow).
ParentState _p = context.findAncestorWidgetOfExactType<ParentState>().data;
_p.setState(() {
_p.data = "some thing";
});
globalState.setState(() {
globalState.name = "some thing";
});
Copy the code
You can either get the parent from the context or you can just assign the parent’s State to a global variable, and you can change the State directly, and you can update it without any problems of course that’s not recommended
reusability
Once we get comfortable with it, we start to see new problems. We might write a lot of components, but some of them can be shared. How can we reuse them
It’s easy to think of hybrid components and higher-order components and hooks that can be implemented in Flutter
mixin
This mixing in flutter is naturally supported by the DART language
mixin _dataStateMixin < T extends StatefulWidget> on State<T> {
var _data = 0;
void_incrementCounter() { setState(() { _data++; }); }}class _CounterState extends State<CounterPage> with _dataStateMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Counter:',
),
Text(
'$_counter',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment', child: Icon(Icons.add), ), ); }}Copy the code
We can easily create a hybrid class in Dart to pull common logic out and reuse it in mixins (note that errors will be reported if the same properties are present)
hoc
As for higher-order components, this is a bit of a shame because we know that the DART used in Flutter is modified to remove reflection so that we can’t create components dynamically, and higher-order components are not possible for the time being
However, if we ignore the condition of dynamic creation, we can use Builder to reuse some logic. Just like HOC, it also has some disadvantages: 1. The state logic of parallel relationships is grouped into parent-child relationships. 2. Multiple levels of nesting can be very difficult to read
hooks
If both approaches are a little uncomfortable, try using Hooks that encapsulate common logic for different life stages within our component
class Example extends StatefulWidget {
final Duration duration;
const Example({Key key, @required this.duration})
: assert(duration ! =null),
super(key: key);
@override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> with SingleTickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: widget.duration);
}
@override
void didUpdateWidget(Example oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.duration != oldWidget.duration) {
_controller.duration = widget.duration;
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
returnContainer(); }}Copy the code
This is a typical scenario where every component that uses AnimationController inevitably writes some logic over and over again during the lifecycle after hooks are used
class Example extends HookWidget {
const Example({Key key, @required this.duration})
: assert(duration ! =null),
super(key: key);
final Duration duration;
@override
Widget build(BuildContext context) {
final controller = useAnimationController(duration: duration);
returnContainer(); }}Copy the code
More fine-grained logic organization and reuse is evident (we can use multiple hooks in the same Widget)
It is a new way of organizing. For example, useState reorganizes the way we useState, useMemoized initializes and caches things, custom hooks, etc. Github.com/rrousselGit…
Of course, hooks are not a panacea, they should only be a way to organize your code logic
conclusion
Today I haven’t talked too much about the FLUTTER API, layout, etc. I just want to share my own ideas from the aspects of components and frameworks combined with some front-end things. I hope I can give some inspiration to front-end developers