El patró Bloc (Business Logic Component) és una de les arquitectures més populars per a la gestió de l'estat en aplicacions Flutter. Aquest patró separa la lògica de negoci de la interfície d'usuari, facilitant la mantenibilitat i la testabilitat del codi.
Objectius del Patró Bloc
- Separació de la lògica de negoci i la interfície d'usuari: Permet que la lògica de negoci es mantingui independent de la interfície d'usuari.
- Reutilització de codi: Facilita la reutilització de la lògica de negoci en diferents parts de l'aplicació.
- Testabilitat: Millora la capacitat de testejar la lògica de negoci de manera aïllada.
Components del Patró Bloc
- Bloc: Conté la lògica de negoci i gestiona els esdeveniments i els estats.
- Esdeveniments: Representen les accions que poden ocórrer en l'aplicació (per exemple, un usuari fa clic en un botó).
- Estats: Representen els diferents estats possibles de la interfície d'usuari.
Implementació del Patró Bloc
- Instal·lació del Paquet
flutter_bloc
flutter_bloc
Per començar a utilitzar el patró Bloc, primer hem d'instal·lar el paquet flutter_bloc
. Afegeix la següent línia al fitxer pubspec.yaml
:
Després, executa flutter pub get
per instal·lar el paquet.
- Creació dels Esdeveniments
Els esdeveniments són accions que poden ocórrer en l'aplicació. Creem una classe abstracta per als esdeveniments i les seves subclasses concretes.
import 'package:equatable/equatable.dart'; abstract class CounterEvent extends Equatable { const CounterEvent(); @override List<Object> get props => []; } class Increment extends CounterEvent {} class Decrement extends CounterEvent {}
- Creació dels Estats
Els estats representen els diferents estats possibles de la interfície d'usuari. Creem una classe abstracta per als estats i les seves subclasses concretes.
import 'package:equatable/equatable.dart'; abstract class CounterState extends Equatable { const CounterState(); @override List<Object> get props => []; } class CounterInitial extends CounterState { final int count; const CounterInitial(this.count); @override List<Object> get props => [count]; }
- Creació del Bloc
El Bloc conté la lògica de negoci i gestiona els esdeveniments i els estats.
import 'package:bloc/bloc.dart'; class CounterBloc extends Bloc<CounterEvent, CounterState> { CounterBloc() : super(const CounterInitial(0)) { on<Increment>((event, emit) { final currentState = state; if (currentState is CounterInitial) { emit(CounterInitial(currentState.count + 1)); } }); on<Decrement>((event, emit) { final currentState = state; if (currentState is CounterInitial) { emit(CounterInitial(currentState.count - 1)); } }); } }
- Integració del Bloc amb la Interfície d'Usuari
Finalment, integrem el Bloc amb la interfície d'usuari utilitzant els widgets BlocProvider
i BlocBuilder
.
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: BlocProvider( create: (context) => CounterBloc(), child: CounterPage(), ), ); } } class CounterPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Counter')), body: Center( child: BlocBuilder<CounterBloc, CounterState>( builder: (context, state) { if (state is CounterInitial) { return Text('Count: ${state.count}'); } return CircularProgressIndicator(); }, ), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: () => context.read<CounterBloc>().add(Increment()), child: Icon(Icons.add), ), SizedBox(height: 8), FloatingActionButton( onPressed: () => context.read<CounterBloc>().add(Decrement()), child: Icon(Icons.remove), ), ], ), ); } }
Exercici Pràctic
Objectiu
Crear una aplicació Flutter que utilitzi el patró Bloc per gestionar un comptador que es pot incrementar i decrementar.
Passos
- Instal·la el paquet
flutter_bloc
. - Defineix els esdeveniments
Increment
iDecrement
. - Defineix l'estat
CounterInitial
. - Implementa el Bloc
CounterBloc
. - Integra el Bloc amb la interfície d'usuari.
Solució
// pubspec.yaml dependencies: flutter: sdk: flutter flutter_bloc: ^8.0.0 // counter_event.dart import 'package:equatable/equatable.dart'; abstract class CounterEvent extends Equatable { const CounterEvent(); @override List<Object> get props => []; } class Increment extends CounterEvent {} class Decrement extends CounterEvent {} // counter_state.dart import 'package:equatable/equatable.dart'; abstract class CounterState extends Equatable { const CounterState(); @override List<Object> get props => []; } class CounterInitial extends CounterState { final int count; const CounterInitial(this.count); @override List<Object> get props => [count]; } // counter_bloc.dart import 'package:bloc/bloc.dart'; class CounterBloc extends Bloc<CounterEvent, CounterState> { CounterBloc() : super(const CounterInitial(0)) { on<Increment>((event, emit) { final currentState = state; if (currentState is CounterInitial) { emit(CounterInitial(currentState.count + 1)); } }); on<Decrement>((event, emit) { final currentState = state; if (currentState is CounterInitial) { emit(CounterInitial(currentState.count - 1)); } }); } } // main.dart import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: BlocProvider( create: (context) => CounterBloc(), child: CounterPage(), ), ); } } class CounterPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Counter')), body: Center( child: BlocBuilder<CounterBloc, CounterState>( builder: (context, state) { if (state is CounterInitial) { return Text('Count: ${state.count}'); } return CircularProgressIndicator(); }, ), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: () => context.read<CounterBloc>().add(Increment()), child: Icon(Icons.add), ), SizedBox(height: 8), FloatingActionButton( onPressed: () => context.read<CounterBloc>().add(Decrement()), child: Icon(Icons.remove), ), ], ), ); } }
Conclusió
El patró Bloc és una eina poderosa per gestionar l'estat en aplicacions Flutter, proporcionant una clara separació entre la lògica de negoci i la interfície d'usuari. Amb aquest patró, podem crear aplicacions més mantenibles, testables i escalables.
Curs de Desenvolupament Flutter
Mòdul 1: Introducció a Flutter
- Què és Flutter?
- Configuració de l'Entorn de Desenvolupament
- Comprensió de l'Arquitectura de Flutter
- Creació de la Teva Primera Aplicació Flutter
Mòdul 2: Conceptes Bàsics de Programació en Dart
- Introducció a Dart
- Variables i Tipus de Dades
- Sentències de Flux de Control
- Funcions i Mètodes
- Programació Orientada a Objectes en Dart
Mòdul 3: Widgets de Flutter
- Introducció als Widgets
- Widgets Stateless vs Stateful
- Widgets Bàsics
- Widgets de Disseny
- Widgets d'Entrada i Formulari
Mòdul 4: Gestió de l'Estat
Mòdul 5: Navegació i Enrutament
- Introducció a la Navegació
- Navegació Bàsica
- Rutes Nomenades
- Passar Dades Entre Pantalles
- Deep Linking
Mòdul 6: Xarxes i APIs
- Obtenir Dades d'Internet
- Analitzar Dades JSON
- Gestió d'Errors de Xarxa
- Ús d'APIs REST
- Integració de GraphQL
Mòdul 7: Persistència i Emmagatzematge
- Introducció a la Persistència
- Preferències Compartides
- Emmagatzematge de Fitxers
- Base de Dades SQLite
- Ús de Hive per a l'Emmagatzematge Local
Mòdul 8: Conceptes Avançats de Flutter
- Animacions en Flutter
- Pintura Personalitzada i Canvas
- Canals de Plataforma
- Isolates i Concurrència
- Optimització del Rendiment
Mòdul 9: Proves i Depuració
- Introducció a les Proves
- Proves Unitàries
- Proves de Widgets
- Proves d'Integració
- Tècniques de Depuració
Mòdul 10: Desplegament i Manteniment
- Preparació per al Llançament
- Construcció per a iOS
- Construcció per a Android
- Integració i Desplegament Continu (CI/CD)
- Manteniment i Actualització de la Teva Aplicació