This source code source flutter_stable_1.22.5

0. Background

  1. For page A (with SingleTickerProviderStateMixinwith TickerProviderStateMixin) Jump to page B and return to the life cycle changes on page A.

1. The Demo code is as follows:

/// Page A:
class TickerPage1 extends StatefulWidget {
  @override
  _TickerPage1State createState() => _TickerPage1State();
}

class _TickerPage1State extends State<TickerPage1> with SingleTickerProviderStateMixin{
 // Test point -0
  // class _TickerPage1State extends State<TickerPage1> with TickerProviderStateMixin{
  AnimationController _animationController;

  @override
  void initState() {
    super.initState();
    print('$runtimeType ----- initState');
    
    // Test point -1
    // _animationController = AnimationController(vsync: this);

    // Test point-2
    // createTicker((_){});
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('$runtimeType ----- didChangeDependencies');
    
    // Test point-3
    // print(TickerMode.of(context));
  }


  @override
  void didUpdateWidget(covariant TickerPage1 oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('$runtimeType ----- didUpdateWidget');
  }

  @override
  void dispose() {
    print('$runtimeType ----- dispose'); _animationController? .dispose();super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print('$runtimeType ----- build');
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: RaisedButton(
          child: Text('TickerPage1'), onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (_) => _TickerPage2())), ), ), ); }}/// Page B
class _TickerPage2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('$runtimeType ----- build');
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Text('TickerPage2'),),); }}Copy the code

Page A has four test points, so try opening any of them and see the printed results.

1. Time-saving versions

  1. Normally without any TickerProviderStateMixin (Single or not), the life cycle callback order is as follows:

Page A —– initState

Page A —– didChangeDependencies

Page A —– build

Page A — push —> Page B

Page B —– build

Page B — pop —> Page A

// nothing

  1. After the Page with A SingleTickerProviderStateMixin changes (such as no AnimtionController relied on TickerProvider) :

Same as conclusion 1 above

  1. Page A with TickerProviderStateMixin changes:

Page A —– initState

Page A —– didChangeDependencies

Page A —– build

Page A — push —> Page B

Page B —– build

Page B — pop —> Page A

Page A —– didChangeDependencies

Page A —– build

  1. After the Page with A SingleTickerProviderStateMixin changes (such as AnimtionController relied on TickerProvider) :

Same as conclusion 3 above

  1. Nature is not whether with SingleTickerProviderStateMixin or with TickerProviderStateMixin (0) test points, Whether an AnimationController is used or a Ticker is created (test points 1, 2) depends on whether TickerMode is bound (test point 3).

  2. Conclusion:SingleTickerProviderStateMixin(after being bound by AnimtionController) orTickerProviderStateMixinWill be indidChangeDependenciesThrough theTickMode.of(context)The binding_EffectiveTickerMode (InheritedWidget type)To check whether the Ticker is in enable state. Therefore, when page A jumps to page B and back again, the TickerMode state changes and builds again (didChangeDependencies –> Build).

2. Source code version

1.ticker_provider.dartFile take a look at the directory structure of the related classes

2. Start with AnimationController

 // Test point -1
 AnimationController _animationController = AnimationController(vsync: this);
    
 AnimationController({
    double? value,
    this.duration,
    this.reverseDuration,
    this.debugLabel,
    this.lowerBound = 0.0.this.upperBound = 1.0.this.animationBehavior = AnimationBehavior.normal,
    required TickerProvider vsync,
  }) : assert(lowerBound ! =null),
       assert(upperBound ! =null),
       assert(upperBound >= lowerBound),
       assert(vsync ! =null),
       _direction = _AnimationDirection.forward {
    _ticker = vsync.createTicker(_tick);
    _internalSetValue(value ?? lowerBound);
  }
Copy the code

Line 18, vsync.createticker (_tick) creates a Ticker in the constructor.

abstract class TickerProvider {
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
  const TickerProvider();

  /// Creates a ticker with the given callback.
  ///
  /// The kind of ticker provided depends on the kind of ticker provider.
  @factory
  Ticker createTicker(TickerCallback onTick);
}
Copy the code

TickerProvider is an abstract class, SingleTickerProviderStateMixin and TickerProviderStateMixin is the realization of the specific subclass respectively.

3, see SingleTickerProviderStateMixin first class

mixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
  Ticker _ticker;

  @override
  Ticker createTicker(TickerCallback onTick) {
    _ticker = Ticker(onTick, debugLabel: kDebugMode ? 'created by $this' : null);
    return _ticker;
  }

  / /... Extraneous code has been removed

  @override
  void didChangeDependencies() {
    if(_ticker ! =null) _ticker.muted = ! TickerMode.of(context);super.didChangeDependencies();
  }
Copy the code

In line 5, a Ticker object is created. This corresponds to the AnimationController initialization _ticker = vsync.createticker (_tick);

Starting at line 13, in the didChangeDependencies method, tickermode.of (context) is called when _ticker is not empty. The createTicker method was called or the AnimationController(vsync: this) was initialized. 2. Tickermode.of (context), which is the core key of this time.

4. Compare the TickerProviderStateMixin class

mixin TickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
  Set<Ticker> _tickers;

  @overrideTicker createTicker(TickerCallback onTick) { _tickers ?? = <_WidgetTicker>{};final _WidgetTicker result = _WidgetTicker(onTick, this, debugLabel: 'created by $this');
    _tickers.add(result);
    return result;
  }
  
  / /... Extraneous code has been removed
  
  @override
  void didChangeDependencies() {
    final boolmuted = ! TickerMode.of(context);if(_tickers ! =null) {
      for (final Ticker ticker in_tickers) { ticker.muted = muted; }}super.didChangeDependencies(); }}class _WidgetTicker extends Ticker {
  _WidgetTicker(TickerCallback onTick, this._creator, { String debugLabel }) : super(onTick, debugLabel: debugLabel);

  final TickerProviderStateMixin _creator;

  @override
  void dispose() {
    _creator._removeTicker(this);
    super.dispose(); }}Copy the code

In line 5, the Ticker object is also created, but a Ticker object of type _WidgetTicker is created and returned, and the local variable _tickers is inserted. This corresponds to _ticker = vsync.createticker (_tick) for each AnimationController initialization; , gets _WidgetTicker (Ticker subtype) and is managed by TickerProviderStateMixin in the collection _tickers.

In line 15, you can see that TickerProviderStateMixin calls tickermode.of (context) directly.

Tickermode.of (context)

class TickerMode extends StatelessWidget {
  const TickerMode({
    Key key,
    @required this.enabled,
    this.child,
  }) : assert(enabled ! =null),
       super(key: key);

 / /... Extraneous code has been removed
 
  static bool of(BuildContext context) {
    final _EffectiveTickerMode widget = context.dependOnInheritedWidgetOfExactType<_EffectiveTickerMode>();
    returnwidget? .enabled ??true;
  }

  @override
  Widget build(BuildContext context) {
    return_EffectiveTickerMode( enabled: enabled && TickerMode.of(context), child: child, ); }}class _EffectiveTickerMode extends InheritedWidget {
  const _EffectiveTickerMode({
    Key key,
    @required this.enabled,
    Widget child,
  }) : assert(enabled ! =null),
        super(key: key, child: child);

  final bool enabled;

 / /... Extraneous code has been removed
}
Copy the code

Line 11, TickerMode) of (context) through the context) type is _EffectiveTickerMode dependOnInheritedWidgetOfExactType lookup control tree Control and establish dependencies (how to establish dependencies is not discussed in this article). The latter is an InheritedWidget type control with an Enable local variable that identifies the current TickerMode state.

The truth is out!

PageA page with SingleTickerProviderStateMixin or with TickerProviderStateMixin will call in the didChangeDependencies life cycle Tickermode. of(context) retrieves the TickerMode state and establishes the dependency (in fact, the wrapper Widget to _EffectiveTickerMode).

6. Why can TickerMode call back different states in the process of page hopping?

Again, the answer is in the big arena of overlays.

class OverlayState extends State<Overlay> with TickerProviderStateMixin {
@override
  Widget build(BuildContext context) {
    // This list is filled backwards and then reversed below before
    // it is added to the tree.
    final List<Widget> children = <Widget>[];
    bool onstage = true;
    int onstageCount = 0;
    for (int i = _entries.length - 1; i >= 0; i -= 1) {
      final OverlayEntry entry = _entries[i];
      if (onstage) {
        onstageCount += 1;
        children.add(_OverlayEntryWidget(
          key: entry._key,
          entry: entry,
        ));
        if (entry.opaque)
          onstage = false;
      } else if (entry.maintainState) {
        children.add(_OverlayEntryWidget(
          key: entry._key,
          entry: entry,
          tickerEnabled: false)); }}return _Theatre(
      skipCount: children.length - onstageCount,
      children: children.reversed.toList(growable: false)); }/ /... Extraneous code has been removed
}
Copy the code

In line 13 and 20, the overlays are finally wrapped in the _OverlayEntryWidget with the “OverlayEntry” that is visible and invisible on the stage but needs to be kept in state.

class _OverlayEntryWidget extends StatefulWidget {
  const _OverlayEntryWidget({
    @required Key key,
    @required this.entry,
    this.tickerEnabled = true,}) :assert(key ! =null),
       assert(entry ! =null),
       assert(tickerEnabled ! =null),
       super(key: key);

  final OverlayEntry entry;
  final bool tickerEnabled;

  @override
  _OverlayEntryWidgetState createState() => _OverlayEntryWidgetState();
}

class _OverlayEntryWidgetState extends State<_OverlayEntryWidget> {
  @override
  Widget build(BuildContext context) {
    return TickerMode(
      enabled: widget.tickerEnabled,
      child: widget.entry.builder(context),
    );
  }

  void _markNeedsBuild() {
    setState(() { /* the state that changed is in the builder */}); }}Copy the code

In line 5, the parameter this.tickerEnabled defaults to true.

In line 21, the OverlayEntry was finally packaged for TickerMode and the state of the Ticker for visibility.

3. The conclusion

Your comments are welcome, and I will try my best to reduce the length of this series to keep it small and beautiful!

The end of the flower, thank you for watching!