aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
SAVED POSTS
aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
aedev.net
No Result
View All Result
Minh họa phát triển ứng dụng mobile đa nền tảng với Flutter

FutureBuilder trong Flutter: đừng tạo Future mới trong build

Suong Nguyen by Suong Nguyen
21/10/2026
in Flutter
0
585
SHARES
3.2k
VIEWS
Summarize with ChatGPTShare to Facebook

Bạn có một màn hình đọc danh sách và nhận thấy tác vụ bất đồng bộ chạy lại sau khi widget cha cập nhật. Một nguyên nhân cần kiểm tra là future: loadItems() đang nằm ngay trong build.

Bài này dành cho người đã biết StatefulWidget, Future và setState. Ví dụ dùng dữ liệu giả để bạn tập trung vào vòng đời widget, không cần một server. Các hành vi được đối chiếu tài liệu Flutter ngày 21/10/2026.

1. Vì sao lời gọi trong build gây rắc rối?

FutureBuilder quan sát một Future; nó không quản lý giúp bạn chính sách lúc nào nên tạo tác vụ mới. Tài liệu FutureBuilder yêu cầu lấy Future trước, chẳng hạn trong initState, thay vì tạo cùng lúc xây widget trong build.

Hãy xem đoạn dễ gây hiểu nhầm này:

FutureBuilder<List<String>>(
  future: loadItems(),
  builder: (context, snapshot) {
    return Text('${snapshot.data?.length ?? 0} mục');
  },
)

Mỗi lần đoạn này được dựng lại, loadItems() có thể tạo một Future mới và khởi động công việc mới. Việc rebuild UI vốn là hoạt động bình thường; bạn cần tách nó khỏi quyết định tải dữ liệu.

2. Giữ Future trong State

Ví dụ dưới đây là một màn hình độc lập. Bạn có thể đặt nó trong một dự án Flutter sẵn có và mở bằng MaterialApp(home: TopicsPage()).

import 'package:flutter/material.dart';

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

  @override
  State<TopicsPage> createState() => _TopicsPageState();
}

class _TopicsPageState extends State<TopicsPage> {
  late Future<List<String>> _topics;

  Future<List<String>> _loadTopics() async {
    await Future<void>.delayed(const Duration(milliseconds: 500));
    return ['Flutter', 'Dart', 'Widget lifecycle'];
  }

  @override
  void initState() {
    super.initState();
    _topics = _loadTopics();
  }

  void _reload() {
    setState(() {
      _topics = _loadTopics();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Chủ đề học tập')),
      body: FutureBuilder<List<String>>(
        future: _topics,
        builder: (context, snapshot) {
          if (snapshot.connectionState != ConnectionState.done) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasError) {
            return Center(
              child: TextButton(
                onPressed: _reload,
                child: const Text('Tải lỗi. Thử lại'),
              ),
            );
          }
          final topics = snapshot.data ?? const <String>[];
          if (topics.isEmpty) {
            return const Center(child: Text('Chưa có chủ đề'));
          }
          return ListView.builder(
            itemCount: topics.length,
            itemBuilder: (context, index) {
              return ListTile(title: Text(topics[index]));
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _reload,
        child: const Icon(Icons.refresh),
      ),
    );
  }
}

initState khởi tạo tác vụ cho State hiện tại. Nút tải lại là một sự kiện rõ ràng để thay Future. setState nhận callback đồng bộ; công việc chờ diễn ra trong _loadTopics, không biến callback thành async. Xem initState và setState.

3. Xử lý khi dữ liệu đầu vào thay đổi

Nếu màn hình nhận userId, tạo Future một lần trong initState chưa đủ: cùng một State có thể nhận widget mới với ID khác. Khi đó, so sánh ID cũ và mới trong didUpdateWidget, rồi thay Future nếu cần.

@override
void didUpdateWidget(covariant UserPage oldWidget) {
  super.didUpdateWidget(oldWidget);
  if (oldWidget.userId != widget.userId) {
    _user = loadUser(widget.userId);
  }
}

Đoạn trên là mẫu ghép vào một UserPage có các trường tương ứng, không phải phần bổ sung trực tiếp cho TopicsPage. Theo tài liệu didUpdateWidget, framework sẽ gọi build sau callback này, nên riêng phép thay Future ở đây không cần thêm setState.

4. Kiểm tra hành vi bằng những thay đổi nhỏ

Thêm log vào _loadTopics để đếm lần bắt đầu tác vụ. Mở màn hình, gây rebuild từ widget cha và quan sát xem số lần tải có tăng ngoài ý muốn không. Sau đó bấm refresh và xác nhận có đúng một tác vụ mới cho sự kiện đó.

Tiếp tục đổi hàm tải để trả danh sách rỗng hoặc ném Exception('demo'). Bạn cần thấy được ba trạng thái riêng: đang tải, tải lỗi và tải thành công nhưng chưa có mục nào. Đây là các bước kiểm tra đề xuất; hãy chạy flutter analyze và widget test trên SDK của dự án trước khi tích hợp.

Lỗi thường gặp

  • Nháy loading khi refresh: ví dụ cố ý hiển thị spinner cho Future mới. Nếu muốn giữ dữ liệu cũ, cần thiết kế UI thể hiện “đang làm mới” riêng.
  • Dữ liệu cũ sau khi đổi tài khoản: kiểm tra đầu vào đã đổi mà Future vẫn giữ từ initState hay chưa.
  • Cho rằng FutureBuilder tự hủy request cũ: đổi Future chỉ thay thứ được widget quan sát; khả năng hủy tác vụ phụ thuộc cơ chế thực hiện request.

Khi có cache, nhiều màn hình dùng chung dữ liệu hoặc cần đồng bộ phức tạp, hãy đặt việc tải vào repository hoặc lớp quản lý state thích hợp. Với một màn hình nhỏ, giữ Future ổn định và quyết định tải lại theo sự kiện là bước đầu dễ kiểm chứng.

Nguồn tham khảo

  • Flutter: FutureBuilder.
  • Flutter: initState.
  • Flutter: setState.
  • Flutter: didUpdateWidget.
Tags: Bất đồng bộDartFlutter
SummarizeShare234
Suong Nguyen

Suong Nguyen

Related Stories

Minh họa phát triển ứng dụng mobile đa nền tảng với Flutter

Tìm kiếm Flutter: debounce và chặn response cũ ghi đè kết quả mới

by Suong Nguyen
22/10/2026
0

Kết hợp Timer, mã phiên truy vấn và kiểm tra mounted để màn hình tìm kiếm không hiển thị nhầm kết quả khi mạng trả về lệch thứ...

Minh họa phát triển ứng dụng mobile đa nền tảng với Flutter

Bắt đầu với Flutter: khi nào nên chọn cho dự án mobile?

by admin
02/07/2026
0

Flutter là lựa chọn đáng cân nhắc khi cần phát triển ứng dụng iOS và Android với một codebase, UI nhất quán và tốc độ ra sản phẩm...

Minh họa so sánh React Native và Flutter cho phát triển mobile

React Native hay Flutter: chọn gì cho sản phẩm mới?

by admin
02/07/2026
0

React Native và Flutter đều mạnh cho mobile cross-platform, nhưng phù hợp với những kiểu team và sản phẩm khác nhau.

Next Post
Minh họa pipeline CI/CD từ commit đến deploy cho team nhỏ

Git bisect: tìm commit gây lỗi bằng một bài kiểm tra nhỏ

0 0 votes
Article Rating
Subscribe
Login
Notify of
guest
guest
0 Comments
Oldest
Newest Most Voted
AE Dev

AE Dev mang đến các bài viết về lập trình, mobile app, backend, frontend, WordPress và công nghệ, phù hợp cho developer muốn học hỏi và cập nhật kiến thức thực tế.

Recent Posts

  • React Native: tránh bàn phím che form với KeyboardAvoidingView và ScrollView
  • Phỏng vấn JavaScript: giải thích event loop, microtask và async/await
  • Deep link mobile an toàn: đúng domain chưa đủ, còn route và quyền đọc

Categories

  • AI
  • Backend
  • Đánh giá
  • DevOps
  • Flutter
  • Hướng dẫn
  • Lập trình Mobile
  • Phỏng vấn
  • React Native
  • Uncategorized

Weekly Newsletter

  • Buy JNews
  • Support Forum
  • Pre-sale Question
  • Contact Us

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In
No Result
View All Result
  • Trang chủ
  • AI
  • Mobile
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
  • Flutter

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

wpDiscuz
0
0
Would love your thoughts, please comment.x
()
x
| Reply