!

Introduction to MVVM + Provider

The MVVM pattern

Provider (Flutter State Management Framework)

The core of Provider is actually InheritedWidget, which is actually the encapsulation of InheritedWidget, so that the InheritedWidget can be used more conveniently by developers in data management.

Inheritedwidgets implement global status updates and don’t solve local problems.

How to use providers

First inherit ChangeNotifier

class BaseProvide with ChangeNotifier {

  CompositeSubscription compositeSubscription = CompositeSubscription();


  /// add [StreamSubscription] to [compositeSubscription]
  ///
  /// It can be disposed of at dispose
  addSubscription(StreamSubscription subscription){
    compositeSubscription.add(subscription);
  }

  @override
  void dispose() {
    super.dispose(); compositeSubscription.dispose(); }}class RegisterPageState extends State<RegisterPage>{
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => RegisterModel(),
      child: switchStatusBar2DarkAppbar(
          context: context,
          title: "provider demo",
          child: Container(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                ChildWidgetA(),
                SizedBox(height: 24,), ChildWidgetB() ], ), ) ) ); }}class ChildWidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    debugPrint('ChildWidgetA build');
    var model = Provider.of<RegisterModel>(context);
    return Container(
      color: Colors.redAccent,
      height: 108,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Text('ChildA', style: new TextStyle(
              color: Colors.black, fontSize: CommounUtil.getSp(15))),
          Text('Model data: ${model.valueA}', style: new TextStyle(
              color: Colors.black, fontSize: CommounUtil.getSp(15))),
          RaisedButton(
            onPressed: () => model.addA(),
            child: Text('add'() [() [() [() }}class ChildWidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    debugPrint('ChildWidgetB build');
    var model = Provider.of<RegisterModel>(context);
    return Container(
      color: Colors.blueAccent,
      height: 108,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Text('ChildB', style: new TextStyle(
              color: Colors.black, fontSize: CommounUtil.getSp(15))),
          Text('Model data: ${model.valueB}', style: new TextStyle(
              color: Colors.black, fontSize: CommounUtil.getSp(15))),
          RaisedButton(
            onPressed: () => model.addB(),
            child: Text('add'() [() [() [() } } I/flutter (12301): ChildWidgetB build
I/flutter (12301): ChildWidgetA build
I/flutter (12301): ChildWidgetB build
I/flutter (12301): ChildWidgetA build
Copy the code

This way both A and B build when A property in the ViewModel changes

A single local refresh Selector

class ChildWidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    debugPrint('ChildWidgetC build');
    return Selector<RegisterModel, int>(
        selector: (context, value) => value.valueA,
        builder: (BuildContext context, value, Widget child) {
          return Container(
            color: Colors.redAccent,
            height: 108,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                Text('ChildC',
                    style: new TextStyle(
                        color: Colors.black, fontSize: CommounUtil.getSp(15))),
                Text('Model data: $value',
                    style: new TextStyle(
                        color: Colors.black, fontSize: CommounUtil.getSp(15))),
                RaisedButton(
                  onPressed: () => context.read<RegisterModel>().addA(),
                  child: Text('add'() [() [() [() }); }}class ChildWidgetD extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    debugPrint('ChildWidgetD build');
    return Container(
      color: Colors.blueAccent,
      height: 108,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Text('ChildD',
              style: new TextStyle(
                  color: Colors.black, fontSize: CommounUtil.getSp(15))),
          Text('Model data: ${context.select((RegisterModel value) => value.valueB)}',
              style: new TextStyle(
                  color: Colors.black, fontSize: CommounUtil.getSp(15))),
          RaisedButton(
            onPressed: () => context.read<RegisterModel>().addB(),
            child: Text('add'() [() [() [() }}Copy the code

With Selector, you can filter out different refresh conditions in the same data model according to the conditions, so that you can avoid the whole data model refresh caused by the transformation of an attribute in the data model.

For details, please refer to pub.flutter-io.cn/packages/pr…

Ii. Project structure

  1. api

    Dart —————— DIO network request initialization encapsulation

    –net_util.dart —————— Get and POST request encapsulation

  2. base

    –app_channel.dart —————— Define different development environments

    –app_config.dart —————— Addresses of different development environments

    — base_provider. Dart — — — — — — — — — — — — — — — — — — the provider base class

    –base_responce.dart —————— Data type returned by the interface

    –base_widge.dart —————— Basic control encapsulation

    — global_provider_manager. Dart — — — — — — — — — — — — — — — — — — a global model of management

  3. Common —————— Public method

  4. Data —————— Interface data parsing

  5. Generated —————— internationalization code

  6. HTTP —————— Interface address

  7. L10n —————— The value is a character string

    — intl_en. Arb in English

    — intl_zh. Arb in Chinese

  8. Model —————— Each page requests model

    –login_repository.dart

  9. res

    –colours. Dart —————— Definition of colour

  10. router

    –application.dart—————— Initializes the router

    — Router_handers.dart —————— defines the route for each page

    Dart —————– Configure the route

  11. UI — — — — — — — — — — — — — — — — — — the UI pages

  12. Utils — — — — — — — — — — — — — — — — — — tools

  13. Viewmodel —————— ViewModel for each page

  14. main_dev.dart,main_test.dart,main_uat.dart,main_online.dart

    —————— Program entry for different development environments

Network request framework Dio

class HttpUtil{

  static HttpUtil instance;
  Dio dio;
  BaseOptions options;

  static HttpUtil getInstance() {
    print('getInstance');
    if (instance == null) {
      instance = new HttpUtil();
    }
    return instance;
  }
  HttpUtil() {
    _initDio();
  }


  _initDio()  {
    final String token = AppConfig.appTools.getToken();
    String baseRequestUrl=Channel.baseURL;
    dio = newDio() .. options = BaseOptions( baseUrl: baseRequestUrl, connectTimeout:20000,
          receiveTimeout: 20000).. interceptors.add(HeaderInterceptor(token)) .. interceptors.add(LogInterceptor(responseBody:true, requestBody: true)); }}class HeaderInterceptor extends Interceptor  {
  String token="";
  HeaderInterceptor(String token){
    this.token=token;
  }
  @override
  onRequest(RequestOptions options) {
    if(token ! =null && token.length > 0) {
      options.headers.putIfAbsent('Authorization', () = >'Bearer' + ' ' + token);
    }
    return super.onRequest(options); }}Copy the code
Stream<BaseResponce> get(String url, {Map<String.dynamic> params}) =>
    Stream.fromFuture(_get(url, params: params)).asBroadcastStream();

Future<BaseResponce> _get(String url, {Map<String.dynamic> params}) async {
  var response = await HttpUtil.getInstance().dio.get(url, queryParameters: params);
  var res = BaseResponce.fromJson(response.data);
  return res;
}


Stream<BaseResponce> post(String url,{dynamic body}) =>
    Stream.fromFuture(_post(url, body)).asBroadcastStream();

Future<BaseResponce> _post(String url, dynamic body) async {
  var response;
  if(body==null){
     response = await HttpUtil.getInstance().dio.post(url);
  }else{
     response = await HttpUtil.getInstance().dio.post(url, data: body);
  }

  var res = BaseResponce.fromJson(response.data);
  if(res.code! =200){

  }
  return res;
}
Copy the code

Detailed address: pub.flutter-io.cn/packages/di…

Iv. Construction of different development environments

There is a main.dart file by default

Dart, main_test.dart, main_uat.dart, and main_online.dart

Each file initializes a different channel:

V. Internationalization

1. Add a dependency configuration

dependencies:
  flutter:
    sdk: flutter
  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^ 0.1.2flutter_localizations: # internationalizationsdk: flutter
Copy the code

2. MaterialApp localizationsDelegates

MaterialApp(
// theme: ThemeData(
// // project configuration font, other theme color configuration can be Baidu
//// fontFamily: Theme.of(context).platform == TargetPlatform.android? (localModel.localeIndex == 1 ? "HanSans":"DIN") : "IOSGILROY",
/ /),
                  debugShowCheckedModeBanner: false,
                  locale: localModel.locale,
                  // Agent of international chemical plant
                  localizationsDelegates: [
                    // Intl plugin (need to be installed) // s.f (context).title
                    S.delegate,
                    RefreshLocalizations.delegate, // Drop refresh
                    // System control internationalization
                    GlobalCupertinoLocalizations.delegate,
                    GlobalMaterialLocalizations.delegate,
                    GlobalWidgetsLocalizations.delegate// Text direction, etc
                  ],
                  supportedLocales: S.delegate.supportedLocales,
                  home: SpalshPage(),
                ),
Copy the code

3. Use localized values

Install the plugin flutterIntl

Restart Flutter Intl under Tool in the menu bar and select Initalize for the Project

useAdd LocaleGenerate arB files for other languages

You can get this string using s.f (context).autobySystem

Six routing.

1. Import the dependency package

Dependencies://segmentfault.com/a/1190000022349982
  fluro: "^ 1.6.3." "
Copy the code

2. Global initialization

final router = new Router();///Initializing a Route
Routes.configureRoutes(router);
Application.router = router;
Copy the code

3. Define the route page

Handler homeHandler= Handler(handlerFunc: (BuildContext context, Map<String.List<String>> parameters){
    return homePage();
});

Handler mineHandler= Handler(handlerFunc: (BuildContext context, Map<String.List<String>> parameters){
  return minePage();
});

Handler loginHandler= Handler(handlerFunc: (BuildContext context, Map<String.List<String>> parameters){
  return LoginPage();
});

Handler xieyiHandler= Handler(handlerFunc: (BuildContext context, Map<String.List<String>> parameters){
  String type=parameters['type'].first;
  return WebViewPage(type: type,);
});

Handler registerHandler= Handler(handlerFunc: (BuildContext context, Map<String.List<String>> parameters){
  return RegisterPage();
});
Copy the code

4. Configure the path

class Routes{
  static String home='/home';
  static String mine='/mine';
  static String login='/login';
  static String xieyi='/xieyi/:type';
  static String register='/register';
  static voidconfigureRoutes(Router router){ router.define(home, handler: homeHandler); router.define(mine, handler: mineHandler); router.define(login, handler: loginHandler); router.define(xieyi, handler: xieyiHandler); router.define(register, handler: registerHandler); }}Copy the code

Detailed address: pub.flutter-io.cn/packages/fl…

Seven rxdart.

Responsive programming

A simple example:

Rx.timer(1.new Duration(milliseconds: 700)).listen((event) {
    requestPerMission(context);
});
Copy the code