Partage de données Flutter InheritedWidget

Il existe différents widgets dans Flutter, y compris l'interface utilisateur et bien sûr des composants fonctionnels. Le composant InheritedWidget est un composant fonctionnel de Flutter. Il peut réaliser le partage de données entre les composants Flutter. La direction de la transmission des données va du haut de l'arborescence des widgets vers le suivant.

InheritedWidget implémente le partage de données de composants

  • Puisque vous voulez utiliser InheritedWidget, écrivez d'abord un Widget pour hériter de InheritedWidget

Implémenter ShareDataWidget

/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/15 0015
/// email: [email protected]
/// des:  InheritedWidget是Flutter中非常重要的一个功能型组件,它提供了一种数据在widget树中从上到下传递、共享的方式
import 'package:flutter/material.dart';


class ShareDataWidget extends InheritedWidget  {


  final int data; //需要在子树中共享的数据,保存点击次数

  ShareDataWidget( {@required this.data,Widget child})
      :super(child:child);


  // 子树中的widget通过该方法获取ShareDataWidget,从而获取共享数据
  static ShareDataWidget of(BuildContext context){
    return context.inheritFromWidgetOfExactType(ShareDataWidget);
  }


  //继承 InheritedWidget 实现的方法 返回值 决定当data发生变化时,是否通知子树中依赖data的Widget 更新数据
  @override
  bool updateShouldNotify(ShareDataWidget oldWidget) {
    //如果返回true,则子树中依赖(build函数中有调用)本widget的子widget的`state.didChangeDependencies`会被调用
    return oldWidget.data != data;
  }
}
复制代码
  • À partir de l'implémentation ci-dessus, nous pouvons voir que la valeur de retour de updateShouldNotify détermine s'il faut notifier le widget dépendant des données dans le sous-arbre pour mettre à jour les données lorsque les données changent, et la méthode of est implémentée pour faciliter le sous-widget pour obtenir le données partagées.

Tester le partage de données ShareDataWidget

  • Nous avons implémenté InheritedWidget plus tôt, voyons maintenant comment utiliser un widget aléatoire pour afficher les données de ShareDataWidget
 /// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/15 0015
/// email: [email protected]
/// des:  测试 ShareDataWidget
import 'package:flutter/material.dart';
import 'package:flutter_hellow_world/InheritedWidget/ShareDataWidget.dart';

class TestShareDataWidget extends StatefulWidget {
  @override
  _TestShareDataWidgetState createState() => _TestShareDataWidgetState();
}

class _TestShareDataWidgetState extends State<TestShareDataWidget> {


  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    //上层 widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
    //如果build中没有依赖InheritedWidget,则此回调不会被调用。
    print("didChangeDependencies");
  }

  @override
  Widget build(BuildContext context) {
    //显示 ShareDataWidget 数据变化,如果build中没有依赖InheritedWidget,则此回调不会被调用。
    return Text(ShareDataWidget.of(context).data.toString());

  }
}
  • Créez ensuite un nouveau widget pour utiliser ShareDataWidget, créez un bouton et incrémentez la valeur de ShareDataWidget à chaque clic.
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/15 0015
/// email: [email protected]
/// des:  创建一个按钮,每点击一次,就将ShareDataWidget的值自增
import 'package:flutter/material.dart';
import 'package:flutter_hellow_world/InheritedWidget/ShareDataWidget.dart';
import 'package:flutter_hellow_world/InheritedWidget/TestShareDataWidget.dart';

class InheritedWidgetTest extends StatefulWidget {
  @override
  _InheritedWidgetTestState createState() => _InheritedWidgetTestState();
}

class _InheritedWidgetTestState extends State<InheritedWidgetTest> {

  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ShareDataWidget(
        data: count, //共享数据 data
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(bottom: 20.0),
              child: TestShareDataWidget()//子widget中依赖ShareDataWidget
            ),
            RaisedButton(
              child: Text("计数增加"),
              onPressed: (){ 
                setState(() {
                  ++ count;
                });
              },
            )
          ],
        ),
      ),
    );
  }
}
  • Le code est très simple. Créez un bouton et ajoutez-en un à la valeur de données de ShareDataWidget chaque fois qu'il est cliqué. Le TestShareDataWidget créé précédemment repose sur la valeur de données de ShareDataWidget. Si les données sont partagées, sa valeur changera en conséquence.

  • résultat courant

appel didChangeDependencies

  • En exécutant l'exemple ci-dessus, nous voyons que le journal suivant sera imprimé dans le journal, ce qui signifie que la méthode didChangeDependencies de TestShareDataWidget est appelée lorsque la valeur de données de ShareDataWidget est modifiée. Cette méthode est rarement utilisée lors de l'écriture de StatefulWidget, nous pouvons l'utiliser dans cette méthode Effectuez certaines opérations chronophages, telles que la persistance des données, les requêtes réseau, etc.
I/flutter ( 7082): didChangeDependencies
  • Si vous ne voulez pas appeler didChangeDependencies pour être appelé, il existe également un moyen de modifier la méthode of de ShareDataWidget comme suit
 // 子树中的widget获取共享数据 方法
  static ShareDataWidget of(BuildContext context){
    //return context.inheritFromWidgetOfExactType(ShareDataWidget);
    //使用 ancestorInheritedElementForWidgetOfExactType 方法当数据变化则不会调用 子widget 的didChangeDependencies 方法 
    return context.ancestorInheritedElementForWidgetOfExactType(ShareDataWidget).widget;
  }
  • Ici, vous pouvez voir que la modification utilise la méthode context.ancestorInheritedElementForWidgetOfExactType, et pourquoi cette méthode didChangeDependencies n'est-elle pas appelée ? Regarder le code source est la meilleure explication, on se tourne directement vers le code source de ces deux méthodes dans framework.dart
/**
 * framework.dart  inheritFromWidgetOfExactType和ancestorInheritedElementForWidgetOfExactType方法源码
 */
 @override
  InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
    if (ancestor != null) {
      assert(ancestor is InheritedElement);
      return inheritFromElement(ancestor, aspect: aspect);
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }


  @override
  InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
    return ancestor;
  }
  • Évidemment, nous pouvons voir que inheritFromWidgetOfExactType appelle la méthode inheritFromElement plus d'une fois, et continuer à regarder le code source de cette méthode.
/**
 * framework.dart  inheritFromElement方法源码
 */
 
@override
  InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }
  • À ce stade, tout devient très clair. La méthode inheritFromWidgetOfExactType appelle la méthode inheritFromElement, et dans cette méthode, le InheritedWidget ajoute une dépendance à son widget enfant, donc lorsque le InheritedWidget change, le widget enfant qui en dépend sera mis à jour, et Appelez la méthode didChangeDependencies que nous venons de mentionner, et la méthode ancestorInheritedElementForWidgetOfExactType n'enregistre pas de dépendances avec le widget enfant et, bien sûr, n'appelle pas la méthode didChangeDependencies.

sommaire

  • Ce qui précède utilise un exemple simple d'utilisation de InheritedWidget pour implémenter l'utilisation de InheritedWidget, comprendre l'appel didChangeDependencies, et on peut dire que vous avez une certaine compréhension du composant InheritedWidget. Ensuite, en encapsulant InheritedWidget, un simple fournisseur est implémenté pour réaliser le partage de données entre les composants.

Mettre en œuvre des composants de partage de données inter-composants

  • En tant que développeur Android natif, le partage de données entre composants ne nous est pas étranger. Par exemple, Eventbus dans le développement Android peut mettre à jour le statut des abonnés aux événements et utiliser SharedPreferences pour la persistance des données. Il y a aussi l'implémentation d'Eventbus dans Flutter, mais ici nous utilisons directement le composant InheritedWidget fourni par Flutter pour réaliser le partage de données entre composants. Le noyau bien connu du fournisseur dans Flutter est également implémenté via InheritedWidget, puis nous implémenterons un simple Fournisseur de notre propre.

Implémenter un InheritedWidget générique

  • Les données à partager sont variées, utilisez des génériques pour déclarer les données à partager
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019-11-17
/// email: [email protected]
/// des:  实现InheritedWidget  保存需要共享的数据InheritedWidget

import 'package:flutter/material.dart';


class InheritedProvider<T> extends InheritedWidget{

  //共享数据  外部传入
  final T data;

  InheritedProvider({@required this.data, Widget child}):super(child:child);

  @override
  bool updateShouldNotify(InheritedProvider<T> oldWidget) {
    ///返回true,则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`方法。
    return true;
  }

}

Enveloppe InheritedWidgetInheritedWidget wrapper

  • Grâce à l'implémentation ci-dessus, vous pouvez voir qu'il n'y a aucun moyen dans le InheritedProvider d'autoriser l'appelant à obtenir le composant InheritedWidget. Ne vous inquiétez pas, voici deux points qui doivent être clarifiés : premièrement, la notification de mise à jour des données utilise ChangeNotifier ( un abonnement d'éditeur de style Flutter fourni par FlultterSDK). Classe de mode abonné) pour notifier, et deuxièmement, après avoir reçu la notification, l'abonné lui-même se met à jour pour reconstruire le InheritedProvider.
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019-11-17
/// email: [email protected]
/// des:  订阅者

import 'package:flutter/material.dart';
import 'package:flutter_theme_change/provider/InheritedProvider.dart';

// 该方法用于在Dart中获取模板类型
Type _typeOf<T>(){
  return T;
}
class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget{


  final Widget child;
  final T data;

  ChangeNotifierProvider({Key key,this.child,this.data});


  //方便子树中的widget获取共享数据
  static T of<T> (BuildContext context,{bool listen = true}){ //listen 是否注册依赖关系 默认注册
    final type = _typeOf<InheritedProvider<T>>();
    final provider = listen ? context.inheritFromWidgetOfExactType(type) as InheritedProvider<T> :
    context.ancestorInheritedElementForWidgetOfExactType(type)?.widget as InheritedProvider<T>;
    return provider.data;

  }


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

class _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>>{

  @override
  Widget build(BuildContext context) {
  //构建 InheritedProvider
    return InheritedProvider<T>(
      data: widget.data,
      child: widget.child,
    );
  }
}
  • À partir du code ci-dessus, un StatefulWidget est créé et la version finale est InheritedProvider. À ce stade, la méthode of qui renvoie les données de données correspondantes est créée et si le contrôle enfant est lié à InheritedWidget peut être défini en définissant (déjà analysé dans la section précédente), de sorte que les contrôles qui modifient les données peuvent être flexibles non liés à InheritedWidget, et il n'est pas nécessaire de mettre à jour les widgets de contrôle qui modifient les données à chaque fois.
  • Ensuite, nous améliorons _ChangeNotifierProviderState. Lorsque les contrôles externes mettent à jour les données et notifient la mise à jour via ChangeNotifier, ChangeNotifierProvider peut se mettre à jour pour que les nouvelles données prennent effet. La méthode de mise à jour consiste à utiliser la méthode setState, qui est également le but de la création de StatefulWidget.
class _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>>{

  @override
  void initState() {
    // 给model添加监听器
    widget.data.addListener(update);
    super.initState();
  }


  @override
  void didUpdateWidget(ChangeNotifierProvider<T> oldWidget) {
    //当Provider更新时,如果新旧数据不"==",则解绑旧数据监听,同时添加新数据监听
    if(widget.data != oldWidget.data){
       oldWidget.data.removeListener(update);
       widget.data.addListener(update);
    }
    super.didUpdateWidget(oldWidget);
  }

  // build方法 省略
  ........

  @override
  void dispose() {
    // 移除model监听器
    widget.data.removeListener(update);
    super.dispose();
  }

  void update() {
    //如果数据发生变化(model类调用了notifyListeners),重新构建InheritedProvider
    setState(() => {

    });
  }

Forfait consommateur de données (Consumer)

  • Les données sont mises à jour, le message est envoyé, et quelqu'un doit les consommer, pour que le modèle abonné-consommateur soit complet.Pour le dire franchement, le nombre de consommations consiste à appeler la méthode of de ChangeNotifierProvider pour obtenir de nouvelles données. l'étape précédente, nous avons déclenché la mise à jour de l'abonné, et indirectement Il va reconstruire son sous-widget, et la reconstruction du sous-widget correspond aux données de consommation, car le consommateur dépend de l'abonné lui-même, regardez le code
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/18 0018
/// email: [email protected]
/// des:  事件 消费者 获得当前context和指定数据类型的Provider
import 'package:flutter/material.dart';
import 'package:flutter_theme_change/provider/ChangeNotifierProvider.dart';

class Consumer<T> extends StatelessWidget{

  final Widget child;
  //获得当前context
  final Widget Function(BuildContext context, T value) builder;

  Consumer({Key key,@required this.builder,this.child}):assert(builder !=null),super(key:key);


  @override
  Widget build(BuildContext context) {  //默认绑定 注册依赖关系
    return builder(context,ChangeNotifierProvider.of<T>(context)); //自动获取Model 获取更新的数据
  }

}
  • À partir du code ci-dessus, la construction du consommateur appelle la méthode ChangeNotifierProvider.of pour enregistrer la dépendance par défaut, de sorte que le widget implémenté par le consommateur mettra à jour les données par la fonction de InheritedWidget.

sommaire

  • Le résumé ci-dessus peut être remplacé par un organigramme

 

Pratique des composants de partage de donnéesToggle Topic

  • Dans la section précédente, j'ai écrit un composant de partage de données de fournisseur très simple basé sur InheritedWidget. Ensuite, j'utilise le ChangeNotifierProvider qui vient d'être écrit à travers un exemple de changement de thème.

  • Le changement de thème ici change simplement la couleur du thème, de sorte que les données partagées sont la valeur de la couleur. L'idée de la démonstration est d'utiliser Dialog pour fournir une couleur de thème sélectionnable, puis de cliquer sur la couleur correspondante pour changer la couleur du thème de l'application, puis de l'implémenter ensemble.

Créer un modèle de thème

  • Le modèle peut également être considéré comme des données partagées, héritant de ChangeNotifier, de sorte que vous pouvez appeler la méthode notifyListeners pour déclencher ChangeNotifierProvider pour recevoir des notifications de modification de données
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/18 0018
/// email: [email protected]
/// des:  主题 model
import 'package:flutter/material.dart';

class ThemeModel extends ChangeNotifier {
  int settingThemeColor ;
  ThemeModel(this.settingThemeColor);

  void changeTheme (int themeColor){
    this.settingThemeColor = themeColor;
    // 通知监听器(订阅者),重新构建InheritedProvider, 更新状态。
    notifyListeners();
  }
}

MaterialApp en tant que widget enfant ChangeNotifierProvider

  • Modifiez la couleur du thème, c'est-à-dire la propriété de thème de MaterialApp, afin que MaterialApp soit utilisé comme sous-widget ChangeNotifierProvider, afin que MaterialApp puisse recevoir la valeur de données de couleur de thème partagée
class _MyHomePageState extends State<MyHomePage> {

  int themeColor =0;
  
  @override
  void initState() {
    super.initState();
    themeColor = sp.getInt(SharedPreferencesKeys.themeColor);
    if(themeColor == null ){
      themeColor = 0xFF3391EA;//默认蓝色
    }
  }
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ChangeNotifierProvider<ThemeModel>(
        data: ThemeModel(themeColor),
        child: Consumer<ThemeModel>(
          builder: (BuildContext context,themeModel){
            return MaterialApp(
              theme: ThemeData(
                primaryColor: Color(themeModel.settingThemeColor),
              ),
              home: Scaffold(
                  appBar: AppBar(
                    title: Text("Flutter Theme Change"),
                    actions: <Widget>[
                      Builder(builder: (context){
                        return IconButton(icon: new Icon(Icons.color_lens), onPressed: (){
                          _changeColor(context);
                        });
                      },)
                      // onPressed 点击事件
                    ],
                  ),
                  body: Center(
                    child: Text("主题变化测试"),
                  )
              ),
            );
          },
        ),
      ),
    );
  }

  void _changeColor(BuildContext context) {
      buildSimpleDialog(context);
  }
  • Ajoutez IconButton à l'AppBar afin qu'il puisse cliquer pour afficher la boîte de dialogue de sélection de couleur. La boîte de dialogue affiche un widget tableau de valeurs de couleur. Chaque widget est implémenté comme suit
class SingleThemeColor extends StatelessWidget {

  final int themeColor;
  final String colorName;

  const SingleThemeColor({Key key,this.themeColor, this.colorName}):
        super(key:key);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () async{
         print("点击了改变主题");
         //改变主题
         ChangeNotifierProvider.of<ThemeModel>(context,listen: false).changeTheme(this.themeColor);
         await SpUtil.getInstance()..putInt(SharedPreferencesKeys.themeColor, this.themeColor);
         Navigator.pop(context);
      },
      child: new Column( // 竖直布局
        children: <Widget>[
           Container(
             width: 50,
             height: 50,
             margin: const EdgeInsets.all(5.0),
             decoration: BoxDecoration( //圆形背景装饰
               borderRadius:BorderRadius.all(
                  Radius.circular(50)
               ),
               color: Color(this.themeColor)
             ),
           ),
           Text(
             colorName,
             style: TextStyle(
               color: Color(this.themeColor),
               fontSize: 14.0),
           ),
        ],
      ),
    );
  }
}
  • Vous pouvez voir que chaque clic sur le widget répond à onTap et appelle ChangeNotifierProvider.of pour obtenir l'objet ThemeModel et appelle la méthode changeTheme pour déclencher la méthode notifyListeners. Il y a aussi quelques détails, comme l'enregistrement de code tel que les valeurs de couleur via SharedPreferences.Pour plus de détails, vous pouvez vérifier l'adresse du code source du projet de démonstration à la fin de l'article.
  • Effet de démonstration en cours d'exécution

 

finalement

Site de démonstration

Je suppose que tu aimes

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