Menu
CoddyTech

Escolhendo onde o estado fica

Parte da seção Gerenciamento de estado do Journey de Flutter da Coddy. Lição 21 de 27.

O botão de modo escuro na tela de configurações se move, mas nada fica escuro. Ele mantém seu valor com setState, e somente o botão consegue acessá-lo. O tema é definido em MyApp, bem longe dali. Esse valor pertence ao app inteiro.

Todo valor em um app precisa de um lugar. Pergunte quem o utiliza:

  • Um widget (o texto de um campo, um painel aberto, o índice de uma aba): setState nesse widget.
  • Alguns widgets próximos (um botão e o rótulo ao lado dele): eleve o estado até o elemento pai deles e passe-o para baixo.
  • Muitos widgets ou várias telas (o usuário, um carrinho, as configurações): um modelo compartilhado com Provider ou Riverpod.

Provider ou Riverpod? Ambos dão conta do recado. Provider é um pouco mais simples e coloca os modelos na árvore de widgets, onde podem ser encontrados por meio de context. Riverpod mantém os providers fora da árvore, então um valor não precisa de context, e os providers podem observar uns aos outros. Escolha um para um app e use-o em todos os lugares.

Aqui, um StateProvider é adequado: um bool, lido por MyApp para escolher o tema e alterado pelo botão:

final darkModeProvider = StateProvider((ref) => false);

// em MyApp, agora um ConsumerWidget:
themeMode: ref.watch(darkModeProvider) ? ThemeMode.dark : ThemeMode.light,
challenge icon

Desafio

Difícil

Faça com que o botão do modo escuro altere o tema do aplicativo inteiro:

  • Adicione final darkModeProvider = StateProvider((ref) => false); no início.
  • Transforme MyApp em um ConsumerWidget cujo themeMode acompanha o provider.
  • Transforme SettingsScreen em um ConsumerWidget: o botão mostra o valor do provider e o define quando é alterado.

Experimente você mesmo

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      themeMode: ThemeMode.light,
      home: const HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Notes'),
        actions: [
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => const SettingsScreen()),
              );
            },
          ),
        ],
      ),
      body: const Center(child: Text('No notes yet')),
    );
  }
}

class SettingsScreen extends StatefulWidget {
  const SettingsScreen({super.key});

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  bool dark = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Settings')),
      body: ListView(
        children: [
          SwitchListTile(
            title: const Text('Dark mode'),
            value: dark,
            onChanged: (value) {
              setState(() {
                dark = value;
              });
            },
          ),
        ],
      ),
    );
  }
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Gerenciamento de estado

Pratique por conta própria: Compilador de Flutter online