2 votes

comment utiliser le contrôleur de flux dans le HookWidget ?

Je suis novice en matière de crochets flottants et de riverpod (gestion des états),

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String _url = "https://owlbot.info/api/v4/dictionary/";
  String _token = "ae7cbdfff57e548a4360348ee519123a741d8e3d";

  TextEditingController _controller = TextEditingController();

  StreamController _streamController;
  Stream _stream;

  Timer _debounce;

  Future _search() async {
    if (_controller.text == null || _controller.text.length == 0) {
      _streamController.add(null);
      return;
    }

    _streamController.add("waiting");
    Response response = await get(_url + _controller.text.trim(),
        headers: {"Authorization": "Token " + _token});
    _streamController.add(json.decode(response.body));
  }

  @override
  void initState() {
    super.initState();

    _streamController = StreamController();
    _stream = _streamController.stream;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Flictionary"),
        bottom: PreferredSize(
          preferredSize: Size.fromHeight(48.0),
          child: Row(
            children: <Widget>[
              Expanded(
                child: Container(
                  margin: const EdgeInsets.only(left: 12.0, bottom: 8.0),
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(24.0),
                  ),
                  child: TextFormField(
                    onChanged: (String text) {
                      if (_debounce?.isActive ?? false) _debounce.cancel();
                      _debounce = Timer(const Duration(milliseconds: 1000), () {
                        _search();
                      });
                    },
                    controller: _controller,
                    decoration: InputDecoration(
                      hintText: "Search for a word",
                      contentPadding: const EdgeInsets.only(left: 24.0),
                      border: InputBorder.none,
                    ),
                  ),
                ),
              ),
              IconButton(
                icon: Icon(
                  Icons.search,
                  color: Colors.white,
                ),
                onPressed: () {
                  _search();
                },
              )
            ],
          ),
        ),
      ),
      body: Container(
        margin: const EdgeInsets.all(8.0),
        child: StreamBuilder(
          stream: _stream,
          builder: (BuildContext ctx, AsyncSnapshot snapshot) {
            if (snapshot.data == null) {
              return Center(
                child: Text("Enter a search word"),
              );
            }

            if (snapshot.data == "waiting") {
              return Center(
                child: CircularProgressIndicator(),
              );
            }

            return ListView.builder(
              itemCount: snapshot.data["definitions"].length,
              itemBuilder: (BuildContext context, int index) {
                return ListBody(
                  children: <Widget>[
                    Container(
                      color: Colors.grey[300],
                      child: ListTile(
                        leading: snapshot.data["definitions"][index]
                                    ["image_url"] ==
                                null
                            ? null
                            : CircleAvatar(
                                backgroundImage: NetworkImage(snapshot
                                    .data["definitions"][index]["image_url"]),
                              ),
                        title: Text(_controller.text.trim() +
                            "(" +
                            snapshot.data["definitions"][index]["type"] +
                            ")"),
                      ),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Text(
                          snapshot.data["definitions"][index]["definition"]),
                    )
                  ],
                );
              },
            );
          },
        ),
      ),
    );
  }
}

Je voulais juste convertir le statefulWidget ci-dessus en HookWidget et comment utiliser riverpod comme statemanagenent pour l'exemple ci-dessus. Je connais quelques bases des hooks et de riverpod, mais je suis toujours confus entre les hooks, statemanagement(riverpod). S'il vous plaît, quelqu'un peut-il m'aider à les comprendre et me donner des exemples ou au moins convertir le code ci-dessus en widget hook, et utiliser hookbuilder.

Merci d'avance

4voto

Alex Hartford Points 1867

Tout d'abord, le code :

final textProvider = StateProvider<String>((_) => '');

final responseFutureProvider =
    FutureProvider.autoDispose.family<Response, String>((ref, text) async {
  if (text == null || text.length == 0) {
    throw Error();
  }

  final String _url = "https://owlbot.info/api/v4/dictionary/";
  final String _token = "ae7cbdfff57e548a4360348ee519123a741d8e3d";

  return await get(_url + text.trim(), headers: {"Authorization": "Token " + _token});
});

final responseProvider = Computed<AsyncValue<Response>>((read) {
  final text = read(textProvider).state;
  return read(responseFutureProvider(text));
});

String _useDebouncedSearch(TextEditingController controller) {
  final search = useState(controller.text);

  useEffect(() {
    Timer? timer;
    void listener() {
      timer?.cancel();
      timer = Timer(
        const Duration(milliseconds: 1000),
        () => search.value = controller.text,
      );
    }

    controller.addListener(listener);
    return () {
      timer?.cancel();
      controller.removeListener(listener);
    };
  }, [controller]);

  return search.value;
}

class MyHomePage extends HookWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final controller = useTextEditingController();
    final text = useProvider(textProvider);
    text.state = _useDebouncedSearch(controller);

    return Scaffold(
      appBar: AppBar(
        title: Text("Flictionary"),
        bottom: PreferredSize(
          preferredSize: Size.fromHeight(48.0),
          child: Row(
            children: <Widget>[
              Expanded(
                child: Container(
                  margin: const EdgeInsets.only(left: 12.0, bottom: 8.0),
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(24.0),
                  ),
                  child: TextFormField(
                    controller: controller,
                    decoration: InputDecoration(
                      hintText: "Search for a word",
                      contentPadding: const EdgeInsets.only(left: 24.0),
                      border: InputBorder.none,
                    ),
                  ),
                ),
              ),
              Icon(
                Icons.search,
                color: Colors.white,
              ),
            ],
          ),
        ),
      ),
      body: MyHomePageBody(),
    );
  }
}

class MyHomePageBody extends HookWidget {
  const MyHomePageBody({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final text = useProvider(textProvider).state;
    final response = useProvider(responseProvider);

    response.when(
      error: (err, stack) => Center(child: Text('Error: $err')),
      loading: () => Center(child: CircularProgressIndicator()),
      data: (response) => ListView.builder(
        itemCount: Response["definitions"].length,
        itemBuilder: (BuildContext context, int index) {
          return ListBody(
            children: <Widget>[
              Container(
                color: Colors.grey[300],
                child: ListTile(
                  leading: response["definitions"][index]["image_url"] == null
                      ? null
                      : CircleAvatar(
                          backgroundImage:
                              NetworkImage(response["definitions"][index]["image_url"]),
                        ),
                  title: Text(text.trim() + "(" + response["definitions"][index]["type"] + ")"),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text(response["definitions"][index]["definition"]),
              )
            ],
          );
        },
      ),
    );
  }
}
  1. Nous ajoutons un fournisseur de texte externe afin de pouvoir lire le champ de texte à partir d'autres fournisseurs.
  2. Nous créons une FutureProviderFamily afin de pouvoir effectuer l'appel API avec un paramètre, le texte de votre champ de texte. Dans Riverpod, les familles permettent de transmettre des paramètres aux fournisseurs.
  3. Nous créons un Computed qui appellera le Future à chaque fois que la valeur du fournisseur de texte change. Il renvoie une AsyncValue qui remplace avantageusement le StreamBuilder que vous utilisiez (nous vous en dirons plus).
  4. J'ai un peu remanié la recherche débondée pour utiliser le hook useEffect. Cela permettra de disposer des ressources pour votre minuterie et de mettre à jour le textProvider si nécessaire. (J'ai appris cela de L'exemple de Remi Marvel )
  5. Nous n'avons plus besoin d'un onChanged ou d'une pression manuelle sur un bouton pour effectuer une recherche, car l'état du textprovider est mis à jour chaque fois que le contrôleur change.
  6. Le corps de votre page a été déplacé dans sa propre classe pour séparer ce qui doit être chargé de ce qui est statique.
  7. Maintenant, au lieu d'un StreamBuilder, nous pouvons utiliser AsyncValue pour gérer les états de chargement, d'erreur et de succès de votre build.

Je sais que cela fait beaucoup de choses à couvrir, je vous recommande donc de vous plonger dans la documentation pour en savoir plus sur tout ce qui se trouve dans cet exemple.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X