Application template method design pattern in flutter page

The page routing jump in this design mode

There is no context nor any routing plugins used here.

Definition and composition of template method pattern

The template method pattern is a very simple pattern that can be implemented using inheritance.

The template method pattern consists of a two-part structure, the first part is an abstract parent class, and the second part is a concrete implementation subclass. Usually, the algorithm framework of the subclass is encapsulated in the abstract parent class, including the implementation of some public methods and encapsulation of the execution order of all methods in the subclass. By inheriting this abstract class, the subclass also inherits the entire algorithm structure, and can choose to override the method of the parent class.

If we have some parallel sub-categories, each sub-category has some similar behaviors and some different behaviors. If the same and different behaviors are mixed in the implementation of each subclass, it means that these same behaviors will be repeated in each subclass.

But in fact, the same behavior can be moved to another single place, and the template method pattern was born to solve this problem. In the template method pattern, the same part of the subclass implementation is moved up to the parent class, and different parts are left for the subclass to implement. This also reflects the idea of ​​generalization.

---- Excerpted from "Javascript Design Patterns and Development Practice"

How to achieve?

First of all, abstract, each page has a route jump method, and the route name must be set when jumping. At the same time they may also execute pop to remove the current page. Then it is necessary to report the entry and exit of users on each page.

The same place lies in the route jump and pop method, as well as user behavior burying points. The difference lies in setting the route name, which needs to be implemented by subclasses.

Based on this, we began to create an abstract class BasePage

/// BasePage 是抽象的页面,所有页面都应该继承于它
/// [ResultType] 是页面 pop 时返回的数据类型。
abstract class BasePage<ResultType> extends StatefulWidget {
  /// 无需context跳转路由的关键,全局导航key
  static final GlobalKey<NavigatorState> navigatorKey = GlobalKey();

  /// 需要子类实现的设置页面名称
  String get pageName;

  /// PageRoute 的 builder
  Widget _pageBuilder(BuildContext context) {
    return this;
  }

  /// 创建一个 PageRoute
  Route<ResultType> createPageRoute(WidgetBuilder builder, RouteSettings settings) {
    return MaterialPageRoute(
      settings: settings,
      builder: builder,
    );
  }

  /// 获取路由设置
  RouteSettings get settings => RouteSettings(name: pageName);

  /// 跳转路由
  Future<ResultType> active({bool replace}) {
    var page = createPageRoute(_pageBuilder, settings);
    assert(page.settings != null); // createPageRoute 函数必须设置 settings
    assert(page.settings.name != null); // settings 中必须包含name。
    if (replace == true) {
      return navigatorKey.currentState.pushReplacement(page);
    } else {
      return navigatorKey.currentState.push(page);
    }
  }
}

复制代码

It's very simple. It can only simply jump at present, but the active() jump function can be easily expanded later to make the jump more flexible. The subpages inherited from it get the corresponding capabilities. The active function here is a template method, because it encapsulates the calling sequence and jump logic.

Well, now we are trying to navigatorKeyregister to MaterialAppgo.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: BasePage.navigatorKey,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: '模板方法设计模式',),
    );
  }
}
复制代码

Oh! Another one BasePageState, StatefulWidget and State always appear in pairs, right? We have the abstract class BasePage inherited from StatefulWidget, but it cannot provide initState and dispose for user behavior embedding. Then we create an abstract classBasePageState

/// 持有 BasePage 实例的 State
abstract class BasePageState<T extends BasePage> extends State<T> {
  
  @mustCallSuper
  @override
  void initState() {
    print('[BasePageState]: 记录用户打开 ${widget.pageName} 页面');
    super.initState();
  }

  @mustCallSuper
  @override
  void dispose() {
    print('[BasePageState]: 记录用户离开 ${widget.pageName} 页面');
    super.dispose();
  }

  /// 封装的 pop。
  bool pop<T extends Object>([T result]) {
    return Navigator.pop<T>(context, result);
  }

}
复制代码

So far, we have written a basic page template.

Then create the first page and let it inherit from the template.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:page_route/base_page.dart';

class PageOne extends BasePage<String> {
  final String title;

  PageOne(this.title);

  @override
  _PageOneState createState() => _PageOneState();

  @override
  String get pageName => 'PageOne';
}

class _PageOneState extends BasePageState<PageOne> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('当前页面是 ${widget.title}'),
              RaisedButton(
                onPressed: () => pop('page one 已结束'),
                child: Text('返回结果'),
              )
            ],
          ),
        ),
      ),
    );
  }

  @override
  void initState() {
    print('${widget.pageName}开始获取数据');
    super.initState();
  }

  @override
  void dispose() {
    print('销毁用不到的对象');
    super.dispose();
  }
}
复制代码

Look, in this case, creating a page is not much different from creating a StatefulWidget in normal times, and it also gains the ability to start routing. So that we can start a page anytime, anywhere. There is no need to write a long list when popping the current page Navigator.pop<T>(context, result).

Personalized routing jump animation

If we want to add some jump animations to a page, we only need to override the template's createPageRoute method in the page.

First create the second page PageTwo, and then realize the fade over

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:page_route/base_page.dart';

class PageTwo extends BasePage {
  @override
  _PageTwoState createState() => _PageTwoState();

  @override
  String get pageName => 'PageTwo';

  @override
  Route createPageRoute(builder, RouteSettings settings) {
    return PageRouteBuilder(
      transitionDuration: Duration(milliseconds: 500), //动画时间为500毫秒
      settings: settings, // 必须载入 settings
      pageBuilder: (BuildContext context, Animation animation,
          Animation secondaryAnimation) {
        return new FadeTransition(
          //使用渐隐渐入过渡,
          opacity: animation,
          child: builder(context), //路由B
        );
      },
    );
  }
}

class _PageTwoState extends BasePageState<PageTwo> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('重写 createPageRoute'),
      ),
      body: Container(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('当前页面的路由动画是 重写 createPageRoute 实现的'),
              RaisedButton(
                onPressed: () => pop('page two 已结束'),
                child: Text('返回结果'),
              )
            ],
          ),
        ),
      ),
    );
  }
}
复制代码

It can be seen that as long as we follow the single responsibility principle, the scalability of the program can be improved to a certain extent. Opening the PageTwo page is as simple as ever.

void goPageTwo() {
  PageTwo().active();
}
复制代码

Create a page quickly

We know that ide provides some live templates. Just enter stful to quickly create a StatefulWidget. We can write a page shortcut template imitating stful. Let's open stful first.

Okay, you have learned to create a shortcut template, so you created a page.

When creating a new shortcut template, you must remember to set the template language.

The code in the screenshot is not complete, the following is the full version:

import 'package:flutter/material.dart';
import 'package:page_route/base_page.dart';

class $PAGE_NAME$ extends BasePage {
  @override
  _$PAGE_NAME$State createState() => _$PAGE_NAME$State();

  @override
  String get pageName => '$PAGE_NAME$';
}

class _$PAGE_NAME$State extends BasePageState<$PAGE_NAME$> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('$END$'),
      ),
      body: Container(),
    );
  }

}
复制代码

At this time, you only need to write page like stful to quickly create a page.

 

Guess you like

Origin blog.csdn.net/u013491829/article/details/109330541