Flutter et InheritedWidget、InheritedModel

Il existe quatre types de widgets dans Flutter

StatelessWidget
StatefullWidget
RenderObjectWidget
InheritedWidget


Parmi eux, StatelessWidget et StatefulWidget sont les plus courants, classés du point de vue de la gestion de l'état. RenderObjectWidget est la classe de base pour tous les widgets qui doivent être rendus.

Quant au dernier InheritedWidget, de nombreux débutants peuvent ne pas le comprendre, mais il doit être utilisé dans certains projets un peu complexes, donc cet article présente l'utilisation de InheritedWidget

Widget hérité

Pour obtenir l'instance la plus proche d'un type particulier de widget hérité à partir d'un contexte de construction, utilisez BuildContext.inheritFromWidgetOfExactType.
Les widgets hérités, lorsqu'ils sont référencés de cette manière, entraîneront la reconstruction du consommateur lorsque le widget hérité lui-même changera d'état.


Dans des circonstances normales, les widgets enfants ne peuvent pas percevoir seuls les changements des widgets parents. Lorsque l'état du parent change, tous les widgets enfants sont reconstruits à travers leurs constructions ;

InheritedWidget peut éviter ce type de création globale et réaliser une mise à jour locale du sous-widget :
le sous-widget obtient de buildContext via BuildContext.inheritFromWidgetOfExactType et écoute le type spécifié de parent InheritedWidget, et se reconstruit après sa reconstruction

insérez la description de l'image ici

Comme indiqué dans la figure ci-dessus, cliquez sur le bouton C, après le changement d'état, le texte de A peut être actualisé séparément et B n'est pas affecté


démo de code

Ensuite, comparez la différence entre utiliser ou non InheritedWidget via le code :

insérez la description de l'image ici

 Cliquez sur +, après que le 0 ci-dessus ait changé, la partie de texte au milieu ne change pas.

mise en œuvre traditionnelle

Après avoir cliqué sur les changements d'état du bouton, widgetA, B, C reconstruira

class TopPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Demo'),
        ),
        body: HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          WidgetA(_counter),
          WidgetB(),
          WidgetC(_incrementCounter),
        ],
      ),
    );
  }
}

class WidgetA extends StatelessWidget {
  final int counter;

  WidgetA(this.counter);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        '${counter}',
        style: Theme.of(context).textTheme.display1,
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('I am a widget that will not be rebuilt.');
  }
}

class WidgetC extends StatelessWidget {
  final void Function() incrementCounter;

  WidgetC(this.incrementCounter);

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      onPressed: () {
        incrementCounter();
      },
      child: Icon(Icons.add),
    );
  }
}


En utilisant les performances Flutter d'AndroidStudio, vous pouvez voir que les widgets A, B et C ont tous participé à la reconstruction.

insérez la description de l'image ici

Implémenté à l'aide de InheritedWidget

import 'package:flutter/material.dart';

class TopPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(
        child: Scaffold(
          appBar: AppBar(
            title: Text('InheritedWidget Demo'),
          ),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              WidgetA(),
              WidgetB(),
              WidgetC(),
            ],
          ),
        ),
      ),
    );
  }
}


class _MyInheritedWidget extends InheritedWidget {

  final HomePageState data;

  _MyInheritedWidget({ Key key, Widget child, this.data }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(_MyInheritedWidget oldWidget) {
    return true;
  }

}

class HomePage extends StatefulWidget {

  final Widget child;

  const HomePage({Key key, @required this.child}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return HomePageState();
  }

  static HomePageState of(BuildContext context, {bool rebuild = true}) {
    if (rebuild) {
      return context.dependOnInheritedWidgetOfExactType<_MyInheritedWidget>().data;
    }
    return context.findAncestorWidgetOfExactType<_MyInheritedWidget>().data;
    // or
    // return (context.getElementForInheritedWidgetOfExactType<_MyInheritedWidget>().widget as _MyInheritedWidget).data;
  }
}

class HomePageState extends State<HomePage> {
  int counter = 0;

  void _incrementCounter() {
    print('HomePageState before _incrementCounter counter $counter');
    setState(() {
      counter++;
      print('HomePageState counter $counter');
    });
  }

  @override
  Widget build(BuildContext context) {
    return _MyInheritedWidget(
      data: this,
      child: widget.child,
    );
  }
}



class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePageState state = HomePage.of(context);

    return Center(
      child: Text(
        '${state?.counter}',
        style: Theme.of(context).textTheme.headline4,
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('I am a widget that will not be rebuilt.');
  }
}

class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePageState state = HomePage.of(context, rebuild: false);

    return RaisedButton(
      onPressed: () {
        state?._incrementCounter();
      },
      child: Icon(Icons.add),
    );
  }
}

Remarque : Cette démo 2022.3.5 a été personnellement vérifiée et la version du SDK Flutter utilisée est la 1.22.4

 Ce qui suit est la démo de la dernière version de flutter sdk, qui prend en charge la sécurité nulle

import 'package:flutter/material.dart';

class TopPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(
        child: Scaffold(
          appBar: AppBar(
            title: Text('InheritedWidget Demo'),
          ),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              WidgetA(),
              WidgetB(),
              WidgetC(),
            ],
          ),
        ),
      ),
    );
  }
}


class _MyInheritedWidget extends InheritedWidget {

  final HomePageState data;

  _MyInheritedWidget({ Key? key, required Widget child, required this.data }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(_MyInheritedWidget oldWidget) {
    return true;
  }

}

class HomePage extends StatefulWidget {

  final Widget child;

  const HomePage({Key? key, required this.child}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return HomePageState();
  }

  static HomePageState? of(BuildContext context, {bool rebuild = true}) {
    if (rebuild) {
      return context.dependOnInheritedWidgetOfExactType<_MyInheritedWidget>()?.data;
    }
    return context.findAncestorWidgetOfExactType<_MyInheritedWidget>()?.data;
    //or
    //return (context.getElementForInheritedWidgetOfExactType<_MyInheritedWidget>().widget as _MyInheritedWidget).data;
  }
}

class HomePageState extends State<HomePage> {
  int counter = 0;

  void _incrementCounter() {
    print('HomePageState before _incrementCounter counter $counter');
    setState(() {
      counter++;
      print('HomePageState counter $counter');
    });
  }

  @override
  Widget build(BuildContext context) {
    return _MyInheritedWidget(
      data: this,
      child: widget.child,
    );
  }
}



class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePageState? state = HomePage.of(context);

    return Center(
      child: Text(
        '${state?.counter}',
        style: Theme.of(context).textTheme.headline4,
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('I am a widget that will not be rebuilt.');
  }
}

class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePageState? state = HomePage.of(context, rebuild: false);

    return RaisedButton(
      onPressed: () {
        state?._incrementCounter();
      },
      child: Icon(Icons.add),
    );
  }
}

On peut voir que lorsque l'état change, widgetB et C ne sont pas reconstruits

insérez la description de l'image ici

Description du code clé

Expliquer les classes clés dans la version InheritedWidget

WidgetA、WidgetC

Dans la version traditionnelle, WidgetA et C transmettent l'état du parent via le constructeur et rappellent la
version InheritedWidget, qui peut être obtenue par les méthodes statiques suivantes

final HomePageState state = HomePage.of(context); // WidgetA
final HomePageState state = HomePage.of(context, rebuild: false);  // WidgetC

WidgetC est un bouton qui doit obtenir la méthode de rappel via l'état, mais n'a pas besoin d'être actualisé avec le changement d'état, donc la reconstruction spécifie false

Ensuite, regardons la méthode statique HomePage.of pour obtenir l'état en détail.

Page d'accueil

static HomePageState of(BuildContext context, {bool rebuild = true}) {
    if (rebuild) {
      return context.dependOnInheritedWidgetOfExactType<_MyInheritedWidget>().data;
    }
    return context.findAncestorWidgetOfExactType<_MyInheritedWidget>().data;
    // or
    // return (context.getElementForInheritedWidgetOfExactType<_MyInheritedWidget>().widget as _MyInheritedWidget).data;
  }


HomePage.of est utilisé pour trouver le _MyInheritedWidget le plus proche via buildContext. Ensuite, vous pouvez obtenir l'état qu'il contient avec _MyInheritedWidget.

Plusieurs méthodes clés pour obtenir le Widget de niveau supérieur sont les suivantes :

méthode la description
inheritFromWidgetOfExactType Récupère le widget parent le plus proche d'un type donné. Le widget doit être une sous-classe de InheritedWidget et enregistrer le contexte entrant avec le widget parent. Lorsque le widget parent change, le widget détenu par ce contexte sera reconstruit pour en obtenir un nouveau à partir de la valeur du widget. C'est ainsi que l'enfant s'enregistre auprès de InheritedWidget.
ancêtreWidgetOfExactType Il n'est utilisé que pour obtenir le widget parent le plus proche d'un type donné et ne sera pas reconstruit en raison des modifications apportées au widget parent
ancêtreInheritedElementForWidgetOfExactType La fonction est la même que inheritFromWidgetOfExactType, mais elle ne recherchera que des sous-classes de InheritedWidget, vous pouvez donc trouver le Widget supérieur avec une complexité O(1)

La méthode ci-dessus est obsolète, la nouvelle version de flutter devrait être :

méthode la description
dependOnInheritedWidgetOfExactType Récupère le widget parent le plus proche d'un type donné. Le widget doit être une sous-classe de InheritedWidget et enregistrer le contexte entrant avec le widget parent. Lorsque le widget parent change, le widget détenu par ce contexte sera reconstruit pour en obtenir un nouveau à partir de la valeur du widget. C'est ainsi que l'enfant s'enregistre auprès de InheritedWidget.
findAncestorWidgetOfExactType Il n'est utilisé que pour obtenir le widget parent le plus proche d'un type donné et ne sera pas reconstruit en raison des modifications apportées au widget parent
getElementForInheritedWidgetOfExactType La fonction est la même que findAncestorWidgetOfExactType, mais elle ne trouvera que des sous-classes de InheritedWidget, vous pouvez donc trouver le Widget supérieur avec une complexité O(1)

Par conséquent, le widgetA se reconstruit lorsque le widget parent change et le widgetB ne se reconstruit pas

class _MyInheritedWidget extends InheritedWidget {
  _MyInheritedWidget({
    Key key,
    @required Widget child,
    @required this.data,
  }) : super(key: key, child: child);

  final HomePageState data;

  @override
  bool updateShouldNotify(_MyInheritedWidget oldWidget) {
    return true;
  }
}


Hérité de InheritedWidget, les widgets enfants peuvent donc être obtenus via dependOnInheritedWidgetOfExactType.

updateShouldNotify contrôle si le widget enfant a besoin de ressentir ses modifications. S'il renvoie true, le widget enfant enregistré via dependOnInheritedWidgetOfExactType sera reconstruit après ses modifications.

Le but ultime du widget enfant est d'obtenir l'état parent partagé, donc l'état est maintenu ici via l'attribut data.

Alors jetez un oeil à ce HomePageState

État de la page d'accueil

  @override
  Widget build(BuildContext context) {
    return _MyInheritedWidget(
      data: this,
      child: widget.child,
    );
  }


L'utilisation de _MyInheritedWidget est la clé ici.

Dans la méthode d'écriture traditionnelle, les widgets A, B et C sont créés directement dans la construction et renvoyés, donc chaque fois que l'état change, les widgets enfants seront recréés et reconstruits ;

Dans la version InheritedWidget, HomePage conserve les enfants du widget parent (TopPage). Lorsque l'état change, les widgets A, B et C ne seront pas reconstruits, mais seront repassés à _MyInheritedWidget, et seul _MyInheritedWidget sera reconstruit.

Page d'accueil

class TopPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ・・・
          body: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[ 
              WidgetA(), // 子widget的创建移动到这里
              WidgetB(),
              WidgetC(),
            ],
          ),
    ・・・
  }
}


Selon la description ci-dessus, afin d'éviter la création et la reconstruction répétées de widgets enfants, déplacez les instanciations des widgets A, B et C ici
 

Modèle hérité

Dans l'exemple ci-dessus, nous spécifions si le sous-Widget participe à la reconstruction en personnalisant le paramètre de reconstruction. En fait, InheritedModel peut également être utilisé pour remplir cette exigence.

InheritedModel hérite de InheritedWidget et peut spécifier un widget enfant spécifique à reconstruire via la clé de chaîne (aspect).

Un bref aperçu de la différence d'implémentation entre la version InheritedModel et la version InheritedWidget

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

   static HomePageState of(BuildContext context, String aspect) {
     return InheritedModel.inheritFrom<_MyInheritedWidget>(context, aspect: aspect).data;
   }
 }


Utilisez InheritedModel.inheritFrom pour obtenir le widget

class _MyInheritedWidget extends InheritedModel {

   @override
   bool updateShouldNotifyDependent(_MyInheritedWidget old, Set aspects) {
     return aspects.contains('A'); // 当aspect包晗“A”时,通知其rebuild
   }
 }


Hériter InheritedModel, remplacer updateShouldNotifyDependent

 class WidgetA extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     final HomePageState state = HomePage.of(context, 'A'); // 注册aspect为“A“



 class WidgetC extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
    final HomePageState state = HomePage.of(context, 'C'); // 注册aspect为“C”


Comme ci-dessus, parce que la clé enregistrée (aspect) est différente, seul widgetA sera notifié de la reconstruction

Le code complet est le suivant :

import 'package:flutter/material.dart';

class TopPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(
        child: Scaffold(
          appBar: AppBar(
            title: Text('InheritedWidget Demo'),
          ),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              WidgetA(),
              WidgetB(),
              WidgetC(),
            ],
          ),
        ),
      ),
    );
  }
}


class _MyInheritedWidget extends InheritedModel {

  final HomePageState data;

  _MyInheritedWidget({ Key key, Widget child, this.data }) : super(key: key, child: child);

  @override
  bool updateShouldNotifyDependent(covariant InheritedModel oldWidget, Set dependencies) {
    return dependencies.contains('A'); // 当dependencies包晗“A”时,通知其rebuild
  }

  @override
  bool updateShouldNotify(covariant InheritedWidget oldWidget) {
    return true;
  }

}

class HomePage extends StatefulWidget {

  final Widget child;

  const HomePage({Key key, @required this.child}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return HomePageState();
  }

  static HomePageState of(BuildContext context, String aspect) {
    return InheritedModel.inheritFrom<_MyInheritedWidget>(context, aspect: aspect).data;

  }
}

class HomePageState extends State<HomePage> {
  int counter = 0;

  void _incrementCounter() {
    print('HomePageState before _incrementCounter counter $counter');
    setState(() {
      counter++;
      print('HomePageState counter $counter');
    });
  }

  @override
  Widget build(BuildContext context) {
    return _MyInheritedWidget(
      data: this,
      child: widget.child,
    );
  }
}



class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePageState state = HomePage.of(context, 'A');

    return Center(
      child: Text(
        '${state?.counter}',
        style: Theme.of(context).textTheme.headline4,
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('I am a widget that will not be rebuilt.');
  }
}

class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePageState state = HomePage.of(context, 'C');
    print('WidgetC counter ${state.counter}');

    return RaisedButton(
      onPressed: () {
        state?._incrementCounter();
      },
      child: Icon(Icons.add),
    );
  }
}


rafraîchissement plus local

Si widgetA est comme suit, nous voulons contrôler davantage le rafraîchissement partiel de ses widgets enfants

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePageState state = HomePage.of(context);

    return Column(
      children: <Widget>[
        Center(
          child: Text(
            '${state.counter}',
            style: Theme.of(context).textTheme.display1,
          ),
        ),
        Text("AAAAA"), // 此处不需rebuild
      ],
    );
  }
}


Si vous comprenez parfaitement le mécanisme d'enregistrement de BuildContext et InheritedWidget, il peut être facilement mis en œuvre :

 return Column(
      children: <Widget>[
        Center(
          child: Builder(builder: (context){
            final HomePageState state = HomePage.of(context);
            return Text(
              '${state.counter}',
              style: Theme.of(context).textTheme.display1,
            );
          }),
        ),
        Text("AAAAA"),
      ],
    );

Créez un widget de classe anonyme via Builder, puis déplacez HomePage.of à l'intérieur. A ce moment, le contexte enregistré dans InheritedWidget n'est plus widgetA mais ce widget de classe anonyme, donc le rafraîchissement partiel de widgetA peut être réalisé


Ne pas utiliser InheritedWidget

Je pense qu'à travers l'introduction ci-dessus, vous devriez pouvoir penser que si le widget enfant ne veut accéder qu'à l'état parent (sans passer de paramètres via le constructeur), mais qu'il n'est pas nécessaire de surveiller ses modifications, vous ne pouvez pas utiliser InheritedWidget :

class TopPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Demo'),
        ),
        body: HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  HomePageState state; // 持有state供子类获取

  @override
  HomePageState createState() {
    state = HomePageState();
    return state;
  }
}

class HomePageState extends State<HomePage> {
  int counter = 0; // 去掉private

  void incrementCounter() { // 去掉private
    setState(() {
      counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          WidgetA(),
          WidgetB(),
          WidgetC(),
        ],
      ),
    );
  }
}

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePage widget = context.ancestorWidgetOfExactType(HomePage); // 获取state
    final HomePageState state = widget?.state;

    return Center(
      child: Text(
        '${state == null ? 0 : state.counter}',
        style: Theme.of(context).textTheme.display1,
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('I am a widget that will not be rebuilt.');
  }
}

class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomePage widget = context.ancestorWidgetOfExactType(HomePage);
    final HomePageState state = widget?.state;

    return RaisedButton(
      onPressed: () {
        state?.incrementCounter();
      },
      child: Icon(Icons.add),
    );
  }
}

Trouvez le type de widget spécifié via ancestorWidgetOfExactType, puis obtenez son état pour l'utiliser. Bien sûr, cette traversée est O(n) et les performances sont moins bonnes que la version InheritedWidget


finalement

De nombreux composants de Flutter sont implémentés sur la base de InheritedWidget, tels que Scoped Model, BLoC (Business Logic of component), etc. Pour maîtriser l'utilisation de ces fonctionnalités avancées, commencez par comprendre InheritedWidget

Code :
https://github.com/vitaviva/flutter_inherited_widget_sample/tree/master/flutter_inherited_widget https://github.com/vitaviva/flutter_inherited_widget_sample/tree/master/flutter_inherited_widget


Extension :
Flutter utilise InheritedModel pour obtenir une actualisation partielle

Le modèle de données défini est

import 'package:flutter/material.dart';
import 'user_type.dart';
 
class UserInheritedModel extends InheritedModel<UserType> {
  final int age;
  final int weight;
 
  const UserInheritedModel(
      {required this.age, required this.weight, required Widget child})
      : super(child: child);
 
  static UserInheritedModel? of(BuildContext context,
      {required UserType aspect}) {
    return InheritedModel.inheritFrom<UserInheritedModel>(context,
        aspect: aspect);
  }
 
  @override
  bool updateShouldNotify(UserInheritedModel old) {
    return age != old.age || weight != old.weight;
  }
 
  @override
  bool updateShouldNotifyDependent(
      UserInheritedModel old, Set<UserType> aspects) {
    return (aspects.contains(UserType.age) && age != old.age) ||
        (aspects.contains(UserType.height) && weight != old.weight);
  }
}

La page à actualiser partiellement est

Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              InkWell(
                child: const AgePage(
                  ideaType: UserType.age,
                ),
                onTap: () {
                  setState(() {
                    _age += 1;
                  });
                },
              ),
              Divider(),
              InkWell(
                child: const WeightPage(
                  ideaType: UserType.height,
                ),
                onTap: () {
                  setState(() {
                    _weight += 1;
                  });
                },
              ),
            ],
          ),

Les pages incluses sont

 
class AgePage extends StatelessWidget {
  final UserType ideaType;
 
  const AgePage({Key? key, required this.ideaType}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    final UserInheritedModel? _ideasTypeIdea =
        UserInheritedModel.of(context, aspect: ideaType);
 
    return Text(
      '${_ideasTypeIdea!.age}\n${Random.secure().nextDouble()}',
    );
  }
}
Une autre page similaire à la précédente


Qu'est-ce que InheritedWidget et comment ça marche dans un Flutter ??

 L'article ci-dessus présente le principe de mise en œuvre de InheritedWidget, et la démo modifiée est publiée ci-dessous pour référence.

import 'package:flutter/material.dart';

class MyStatefulWidget extends StatefulWidget {
  final Widget child;

  const MyStatefulWidget({Key key, @required this.child}) : super(key: key);

  static MyStatefulWidgetState of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>().data;
  }

  @override
  State<StatefulWidget> createState() {
    return MyStatefulWidgetState();
  }
}

class MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _counterValue = 0;

  int get counterValue => _counterValue;

  void addCounterBy() {
    setState(() {
      _counterValue += 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MyInheritedWidget(
      child: widget.child,
      data: this,
    );
  }
}

class MyInheritedWidget extends InheritedWidget {
  final MyStatefulWidgetState data;

  MyInheritedWidget({
    Key key,
    @required Widget child,
    @required this.data,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return true;
  }
}

class MyContainer extends StatelessWidget {
  final Widget child;

  MyContainer({
    Key key,
    @required this.child,
  })  : super(key: key);

  void onPressed(BuildContext context) {
    MyStatefulWidget.of(context).addCounterBy();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        width: 200,
        height: 200,
        child: RaisedButton(
          color: Colors.red,
          onPressed: (){
            onPressed(context);
          },
          child: child,
        ),
      ),
    );
  }
}

class DummyContainer extends StatelessWidget {
  final Widget child;

  const DummyContainer({Key key, this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return child;
  }
}

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      "Counter",
      style: TextStyle(
        color: Colors.white,
        fontSize: 20,
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text('I am a widget that will not be rebuilt.',
      style: TextStyle(
        fontSize: 14,
        color: Colors.white,
      ),);
  }
}

class WidgetC extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return CounterValueState();
  }
}

class CounterValueState extends State<WidgetC> {
  int counterValue;
  double fontSize;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    MyStatefulWidgetState data = MyStatefulWidget.of(context);
    counterValue = data.counterValue;
    fontSize = 50.0 + counterValue;
  }

  @override
  Widget build(BuildContext context) {
    return Text(
      "$counterValue",
      style: TextStyle(
        fontSize: fontSize,
        color: Colors.white,
      ),
    );
  }
}

Je suppose que tu aimes

Origine blog.csdn.net/jdsjlzx/article/details/123263801
conseillé
Classement