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_blocPer 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
IncrementiDecrement. - 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ó
