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

Flutter RefreshIndicator: kéo làm mới cả danh sách ngắn

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

Màn hình có vài dòng nên chưa đủ cao để cuộn. Bạn đã bọc ListView bằng RefreshIndicator, nhưng kéo xuống vẫn không thấy vòng làm mới. Trước khi sửa API hoặc thêm state loading, hãy kiểm tra khả năng nhận thao tác kéo của chính scrollable.

Bài đối chiếu Flutter API hiện hành ngày 02/11/2026. Ví dụ đã rà thủ công theo hợp đồng widget và lifecycle; chưa build bằng Flutter SDK, chưa chạy widget test hay thiết bị. Phần danh sách, thời gian chờ và lỗi là dữ liệu mô phỏng cục bộ, không phải một lần gọi mạng đã được đo.

Hai điều kiện khác nhau

RefreshIndicator cần một scroll view theo chiều dọc và tín hiệu overscroll phù hợp. Khi nội dung ngắn hơn viewport, thêm AlwaysScrollableScrollPhysics vào ListView để vẫn cho người dùng thực hiện thao tác cuộn. Physics này không tự thay thế toàn bộ hành vi overscroll của nền tảng.

Điều kiện tiếp theo là callback onRefresh trả về Future đại diện cho công việc tải. Indicator dựa vào Future ấy để biết lúc kết thúc. Nếu gọi một hàm async rồi bỏ Future, spinner có thể kết thúc sớm hơn công việc mà người dùng đang chờ.

Demo tối thiểu với hai dòng dữ liệu

Có thể thay lib/main.dart trong project Flutter Material thử nghiệm bằng ví dụ sau:

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: RefreshDemo()));

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

  @override
  State<RefreshDemo> createState() => _RefreshDemoState();
}

class _RefreshDemoState extends State<RefreshDemo> {
  int revision = 0;
  bool failNext = false;
  String? error;

  Future<void> refresh() async {
    final shouldFail = failNext;
    try {
      await Future<void>.delayed(const Duration(milliseconds: 500));
      if (shouldFail) throw StateError('Demo failure');
      if (!mounted) return;
      setState(() {
        revision += 1;
        error = null;
      });
    } catch (_) {
      if (!mounted) return;
      setState(() => error = 'Chưa làm mới được. Hãy kéo để thử lại.');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Kéo làm mới')),
      body: RefreshIndicator(
        onRefresh: refresh,
        child: ListView(
          physics: const AlwaysScrollableScrollPhysics(),
          padding: const EdgeInsets.all(16),
          children: [
            Text('Phiên dữ liệu: $revision'),
            const ListTile(title: Text('Dòng minh họa')),
            SwitchListTile(
              title: const Text('Mô phỏng lỗi khi làm mới'),
              value: failNext,
              onChanged: (value) => setState(() => failNext = value),
            ),
            if (error != null) Text(error!),
          ],
        ),
      ),
    );
  }
}

Biến shouldFail chụp cấu hình tại lúc bắt đầu lượt để việc đổi switch giữa chừng không đổi kết quả của lượt đang chờ. revision chỉ tăng khi thành công; lỗi được chuyển thành thông báo trên UI và Future vẫn hoàn thành sau xử lý. Bài không ghi nhận một lỗi mạng thật nào.

Kiểm tra theo những trạng thái có ý nghĩa

Trên project thử, kéo từ đầu danh sách ngắn và kiểm tra indicator xuất hiện; sau khi hoàn thành, revision tăng đúng một lượt. Bật mô phỏng lỗi, kéo lại: số phiên giữ nguyên, thông báo lỗi hiển thị, indicator kết thúc. Tắt lỗi rồi thử lại để kiểm tra thông báo cũ được xóa khi thành công.

Hãy thử thêm màn hình nhỏ, danh sách dài và rời màn hình trong lúc Future còn chờ. Những case này là kế hoạch kiểm tra trên thiết bị, chưa được thực thi trong bài. mounted bảo vệ lần cập nhật UI sau await; nó không hủy tác vụ nền hay request HTTP nếu bạn thay phần chờ bằng mạng thật.

Lỗi thường gặp khi tích hợp

Đặt physics trên widget bọc ngoài mà không đặt trên scrollable con sẽ không giải quyết đúng chỗ. Bọc một Column không cuộn cũng không tạo nguồn scroll notification cần thiết. Demo dùng Scaffold.body cho ListView một vùng cao hữu hạn; nếu chuyển vào Column khác, cần giữ constraint phù hợp.

Với danh sách rỗng, vẫn giữ scrollable và hiển thị empty state bên trong thay vì thay cả cây bằng một Text tĩnh. Với nested scroll, đừng sao chép nguyên demo rồi kết luận notification đúng cấp: cần xem thêm notificationPredicate theo cấu trúc thật.

Demo chỉ có một đường gọi refresh từ indicator. Nếu còn nút tải lại hoặc nguồn cập nhật khác, cần chính sách tránh các response cũ ghi đè dữ liệu mới. Giải quyết khả năng kéo và vòng đời Future trước, rồi mới bổ sung luồng tải phức tạp hơn.

Nguồn tham khảo

  • Flutter: RefreshIndicator và danh sách không đủ dài
  • Flutter: AlwaysScrollableScrollPhysics
  • Flutter: State.mounted

Tags: FlutterRefreshIndicatorScrolling
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

Flutter SafeArea: minimum và MediaQuery padding không phải cộng tay

by Suong Nguyen
01/11/2026
0

Đặt SafeArea đúng cấp, hiểu minimum và lý do MediaQuery.padding thay đổi trong subtree đã được xử lý inset.

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

Flutter Dismissible: xác nhận rồi xóa đúng dòng theo ID

by Suong Nguyen
31/10/2026
0

Tách confirmDismiss và onDismissed để hủy thao tác vuốt hoặc xóa đúng mục khỏi danh sách local.

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

Flutter IndexedStack: giữ state riêng khi chuyển giữa hai tab

by Suong Nguyen
30/10/2026
0

Tạo hai bộ đếm trong hai tab để kiểm tra state được giữ trong cây widget và hiểu chi phí của việc giữ các trang sống.

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

Flutter LayoutBuilder: chọn layout theo vùng cha thay vì toàn cửa sổ

by Suong Nguyen
29/10/2026
0

Đặt một component vào vùng rộng 360 logical pixel để thấy vì sao kích thước cửa sổ không thay thế constraints của cha.

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

Git show: đọc file ở HEAD và index mà không checkout

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 onLayout: đo vùng card thay vì lấy chiều rộng cửa sổ
  • Object.freeze chỉ đóng băng nông: giải thích bằng assertion
  • Android getString: dùng placeholder có thứ tự để dịch nguyên câu

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