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):
setStatenesse 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,Desafio
DifícilFaç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
MyAppem umConsumerWidgetcujothemeModeacompanha o provider. - Transforme
SettingsScreenem umConsumerWidget: 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;
});
},
),
],
),
);
}
}
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
1Elevando o estado
Passar o estado para um componente filhoPassar um callback para um componente filhoCallbacks que enviam um valorElevar o estado4Riverpod
Riverpod: ProviderScopeConsumerWidget e ref.watchRiverpod: StateProviderRiverpod: NotifierProviders que observam ProvidersEscolhendo onde o estado ficaPratique por conta própria: Compilador de Flutter online