openplanning

Flutter BloC cho người mới bắt đầu

  1. Cài đặt thư viện
  2. Ví dụ Counter
  3. Ví dụ với kiểu Object
  4. Ví dụ với kiểu List

1. Cài đặt thư viện

pubspec.yaml
dependencies:
  flutter_bloc: ^8.1.6

2. Ví dụ Counter

Chúng ta sẽ bắt đầu ví dụ "Counter" phiên bản Flutter BloC. Trong ví dụ này, lớp CounterCubit mở rộng từ lớp Cubit<int>, điều này có nghĩa là CounterCubit đang quản lý một biến "state" với kiểu dữ liệu là int.
counter_cubit.dart
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);

  void increment() => emit(state + 1);
  void decrement() => emit(state - 1);
}
main_counter.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'counter_cubit.dart';
import 'counter_page.dart';

void main() {
  runApp(const CounterApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: BlocProvider(
        create: (BuildContext context) => CounterCubit(),
        child: const CounterPage(),
      ),
    );
  }
}
Trong đoạn code trên, BlocProvider sẽ khởi tạo đối tượng CounterCubit và tiêm nó vào CounterPage.
MaterialApp(
  home: BlocProvider(
    create: (_) => CounterCubit(),
    child: const CounterPage(),
  ),
)
Trong CounterPage bạn có thể lấy được đối tượng CounterCubit và thay đổi trạng thái của nó.
counter_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'counter_cubit.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Bloc Demo'),
        backgroundColor: Colors.blue,
      ),
      body: BlocBuilder<CounterCubit, int>(
        builder: (context, count) => Center(
          child: Text('Count: $count'),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          // Get CounterCubit object.
          CounterCubit counterCubit = context.read<CounterCubit>();
          // Change the state of counterCubit
          counterCubit.increment();
        },
      ),
    );
  }
}

3. Ví dụ với kiểu Object

Ở ví dụ trên chúng ta đã sử dụng Cubit<int>. Về cơ bản int, double, num, boolString là các kiểu dữ liệu cơ bản, vì vậy ví dụ trên đã hoạt động hoàn hảo. Với các kiểu dữ liệu Object sẽ có một vài điều cần lưu ý nếu không nó sẽ không hoạt động như mong đợi.
product.dart
class Product {
  int id;
  String name;
  double price;
  double taxPercentage;

  Product({
    required this.id,
    required this.name,
    required this.price,
    required this.taxPercentage,
  });

  // Return a copy of the product.
  Product copy() {
    return Product(
      id: id,
      name: name,
      price: price,
      taxPercentage: taxPercentage,
    );
  }
}

Product getExampleProduct() {
  return Product(
    id: 1,
    name: "Samsung Galaxy S24 Ultra",
    price: 1799.99,
    taxPercentage: 0,
  );
}
Trong ví dụ này, ProductCubit quản lý một biến "state" với kiểu dữ liệu là Product:
product_cubit.dart
import 'package:flutter_bloc/flutter_bloc.dart';

import 'product.dart';

class ProductCubit extends Cubit<Product> {
  ProductCubit(super.product);

  void increasePrice() {
    // IMPORTANT:
    Product copy = state.copy();
    copy.price = copy.price + 1;
    // Emit a new state.
    emit(copy);
  }

  void increaseTaxPercentage() {
    // IMPORTANT:
    Product copy = state.copy();
    copy.taxPercentage = copy.taxPercentage + 1;
    // Emit a new state.
    emit(copy);
  }
}
Chú ý: Nếu "state" là một Object. Việc gọi emit(state) sẽ chỉ hoạt động với lần gọi đầu tiên.
Work!!
void increasePrice() {
    // IMPORTANT:
    Product copy = state.copy();
    copy.price = copy.price + 1;
    // Emit a new state.
    emit(copy);
 }
Not Work!!
void increasePrice() {
  state.price = state.price + 1;
  // Emit --> only works the first time.
  emit(state);
}
main_product.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'product.dart';
import 'product_cubit.dart';
import 'product_page.dart';

void main() {
  runApp(const ProductApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: BlocProvider(
        create: (BuildContext context) {
          Product product = getExampleProduct();
          //
          return ProductCubit(product);
        },
        child: const ProductPage(),
      ),
    );
  }
}
BlocProvider khởi tạo đối tượng ProductCubit và tiêm nó vào ProductPage.
MaterialApp( 
  home: BlocProvider(
    create: (BuildContext context) {
      Product product = getExampleProduct();
      //
      return ProductCubit(product);
    },
    child: const ProductPage(),
  ),
)
product_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'product.dart';
import 'product_cubit.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Bloc Demo'),
        backgroundColor: Colors.blue,
      ),
      body: BlocBuilder<ProductCubit, Product>(
        builder: (context, product) => Center(
          child: Column(
            children: [
              Text("ID: ${product.id}"),
              Text("Name: ${product.name}"),
              Text("Price: ${product.price}"),
              Text("Tax Percentage: ${product.taxPercentage}"),
              TextButton(
                child: const Text("Increase Price"),
                onPressed: () {
                  ProductCubit pc = context.read<ProductCubit>();
                  pc.increasePrice();
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}
  • Flutter BloC Cubit Equatable

4. Ví dụ với kiểu List

employee.dart
class Employee {
  int id;
  String name;
  double salary;

  Employee({
    required this.id,
    required this.name,
    required this.salary,
  });
}

List<Employee> getExampleEmployees() {
  return [
    Employee(id: 1, name: "Tom", salary: 1000),
    Employee(id: 2, name: "Jerry", salary: 1100),
  ];
}
employee_list_cubit.dart
import 'package:flutter_bloc/flutter_bloc.dart';

import 'employee.dart';

class EmployeeListCubit extends Cubit<List<Employee>> {
  EmployeeListCubit(super.employees);

  void increaseSalary(Employee emp) {
    emp.salary = emp.salary + 1;
    // IMPORTANT:
    List<Employee> newState = [...state];
    emit(newState);
  }
}
Phương thức emit() không thể phát hiện được sự thay đổi trạng thái của một phần tử trong một danh sách. Vì vậy bạn cần phải gọi phương thức emit() cho một danh sách mới là bản sao của danh sách hiện tại.
employee_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'employee.dart';
import 'employee_list_cubit.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Bloc Demo'),
        backgroundColor: Colors.blue,
      ),
      body: BlocBuilder<EmployeeListCubit, List<Employee>>(
        builder: (context, employees) => ListView(
          children: employees
              .map(
                (emp) => ListTile(
                  title: Text(emp.name),
                  subtitle: Text(emp.salary.toString()),
                  trailing: TextButton.icon(
                    onPressed: () {
                      EmployeeListCubit cubit =
                          context.read<EmployeeListCubit>();
                      cubit.increaseSalary(emp);
                    },
                    icon: const Icon(
                      Icons.add,
                    ),
                    label: const Text("Increase Salary"),
                  ),
                ),
              )
              .toList(),
        ),
      ),
    );
  }
}
main_employee.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'employee.dart';
import 'employee_list_cubit.dart';
import 'employee_page.dart';

void main() {
  runApp(const EmployeeApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: BlocProvider(
        create: (BuildContext context) {
          List<Employee> employees = getExampleEmployees();
          //
          return EmployeeListCubit(employees);
        },
        child: const EmployeePage(),
      ),
    );
  }
}

Các hướng dẫn lập trình Flutter

Show More