SliverFillRemainingBoxAdaptor: Kẻ Lấp Đầy Khoảng Trống Trong Flutter!
Flutter

SliverFillRemainingBoxAdaptor: Kẻ Lấp Đầy Khoảng Trống Trong Flutter!

Author

Admin System

@root

Ngày xuất bản

21 Mar, 2026

Lượt xem

6 Lượt

"SliverFillRemainingBoxAdaptor"

Anh em Gen Z mê code ơi, hôm nay anh Creyt sẽ dắt tụi em đi “bóc phốt” một thằng cha khá thú vị trong hội “Sliver” của Flutter: SliverFillRemainingBoxAdaptor. Nghe tên dài ngoằng, khó nuốt đúng không? Yên tâm, anh em mình sẽ biến nó thành món gà rán giòn tan, dễ hiểu cực kỳ!

1. SliverFillRemainingBoxAdaptor là gì? Để làm gì mà nó “ngầu” vậy?

Để hiểu thằng cha này, trước hết mình phải hiểu “Sliver” là gì cái đã. Tưởng tượng một cái cuộn phim (scroll view) dài ngoằng của tụi em đó, thì mỗi phân đoạn trên cuộn phim đó chính là một “Sliver”. Thay vì dùng mấy cái widget “full-size” như ListView hay SingleChildScrollView mà nó cứ render tùm lum tà la, thì CustomScrollView kết hợp với các Sliver sẽ chỉ render những gì thực sự cần thiết trên màn hình thôi. Tiết kiệm tài nguyên vãi chưởng!

Thế còn SliverFillRemainingBoxAdaptor? À, thằng này nó là “kẻ lấp đầy khoảng trống còn lại” của cái cuộn phim đó. Nghe nó cứ “chiếm hữu” sao đó ha? Đúng vậy! Tưởng tượng tụi em có một CustomScrollView mà nội dung bên trên nó ngắn ngủn, không đủ lấp đầy màn hình. Thay vì để một khoảng trắng “vô duyên” ở dưới, thì thằng cha SliverFillRemainingBoxAdaptor này sẽ nhảy vào, chiếm trọn phần không gian còn trống đó và “ôm” lấy widget con của nó. Nó giống như cái ông hàng xóm nhiệt tình quá mức, thấy nhà mình còn trống cái gì là ổng mang đồ qua lấp đầy hết vậy đó!

Mục đích chính của nó: Đảm bảo một widget (ví dụ: nút "Thêm vào giỏ hàng", thanh nhập tin nhắn chat, hoặc một cái footer) luôn luôn hiển thị ở cuối vùng cuộn và chiếm trọn phần không gian còn lại nếu các nội dung khác không đủ dài để lấp đầy.

2. Code Ví Dụ Minh Họa Rõ Ràng, Chuẩn Kiến Thức

Thôi lý thuyết đủ rồi, giờ mình đi vào thực chiến cho máu! Anh em xem ví dụ này để thấy thằng cha SliverFillRemainingBoxAdaptor nó hoạt động như thế nào nhé:

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Creyt\'s Sliver Demo',
      theme: ThemeData(
        primarySwatch: Colors.blueGrey,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const SliverFillRemainingDemo(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Creyt dạy SliverFillRemaining'),
      ),
      body: CustomScrollView(
        slivers: <Widget>[
          // Header cứng đầu, chỉ cao 100px
          SliverToBoxAdapter(
            child: Container(
              height: 100.0,
              color: Colors.amber[200],
              alignment: Alignment.center,
              child: const Text(
                'Đây là Header (SliverToBoxAdapter)',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
            ),
          ),

          // Danh sách các item ngắn ngủn
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return Container(
                  height: 50.0,
                  color: index % 2 == 0 ? Colors.blue[100] : Colors.blue[200],
                  alignment: Alignment.center,
                  child: Text('Item ${index + 1}'),
                );
              },
              childCount: 5, // Chỉ có 5 item, không đủ lấp đầy màn hình
            ),
          ),

          // Chính nó đây rồi: Kẻ lấp đầy khoảng trống!
          SliverFillRemaining(
            // hasScrollBody: true, // Thử bật cái này nếu nội dung bên trong cũng cần cuộn
            child: Container(
              color: Colors.green[100],
              alignment: Alignment.center,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const Text(
                    'Đây là phần còn lại được lấp đầy (SliverFillRemaining)',
                    textAlign: TextAlign.center,
                    style: TextStyle(fontSize: 16, color: Colors.green),
                  ),
                  const SizedBox(height: 10),
                  ElevatedButton(
                    onPressed: () {
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(content: Text('Nút này nằm ở cuối!')),
                      );
                    },
                    child: const Text('Nút hành động ở cuối trang'),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Giải thích code:

  • Chúng ta có một CustomScrollView chứa các slivers.
  • SliverToBoxAdapter: Dùng để đưa một widget RenderBox (như Container) vào làm Sliver. Ở đây là cái Header cao 100px.
  • SliverList: Tạo một danh sách các item. Anh em thấy đó, anh Creyt chỉ cho 5 item thôi, nên nó không đủ dài để lấp đầy màn hình.
  • SliverFillRemaining: Đây là nhân vật chính của chúng ta. Nó sẽ "ngốn" hết phần không gian còn lại trên màn hình sau khi SliverToBoxAdapterSliverList đã render. Bên trong nó, anh em có thể đặt bất kỳ widget nào, ví dụ như cái Container màu xanh lá cây với một cái nút hành động đó.

Khi chạy code này, dù danh sách chỉ có 5 item ngắn ngủn, cái Container màu xanh lá cây chứa nút bấm vẫn sẽ luôn luôn nằm ở cuối màn hình và chiếm trọn phần không gian thừa ra. Đỉnh của chóp!

Illustration

3. Mẹo (Best Practices) để ghi nhớ hoặc dùng thực tế

  • "Thằng cha tham lam": Luôn nhớ SliverFillRemainingBoxAdaptor là một thằng rất "tham lam". Nó sẽ lấy toàn bộ không gian còn lại mà không hỏi ý kiến ai. Đừng đặt nó ở giữa một đống Sliver khác mà anh em muốn kiểm soát chiều cao chặt chẽ, dễ bị vỡ layout lắm!
  • Bạn thân của CustomScrollView: Nó chỉ có ý nghĩa khi dùng trong CustomScrollView (hoặc các widget dựa trên Sliver khác). Đừng cố nhét nó vào ListView hay Column thường, nó sẽ không hoạt động đúng đâu.
  • hasScrollBody - Cái này hay nè!: Mặc định, SliverFillRemaining sẽ không cho phép nội dung bên trong nó tự cuộn. Nếu nội dung bên trong SliverFillRemaining của tụi em cũng cần cuộn (ví dụ: một ListView con bên trong nó), hãy set hasScrollBody: true. Khi đó, SliverFillRemaining sẽ tự nó quản lý việc cuộn của nội dung con, và nó sẽ chỉ cuộn khi toàn bộ CustomScrollView đã cuộn hết các Sliver khác.
  • Phân biệt với SliverToBoxAdapter: SliverToBoxAdapter dùng khi anh em muốn một widget có chiều cao cố định. SliverFillRemaining dùng khi anh em muốn một widget chiếm phần không gian còn lại.

4. Học thuật sâu của anh Creyt: "BoxAdaptor" là gì?

Trong Flutter, mọi thứ đều là widget, và đằng sau mỗi widget là một RenderObject chịu trách nhiệm vẽ và bố cục. Các Sliver cũng vậy, chúng có RenderSliver riêng. SliverFillRemainingBoxAdaptor là một Sliver đặc biệt, nó có nhiệm vụ chuyển đổi một RenderBox thông thường (như Container, Column, Row, Text,...) thành một RenderSliver. Cái đuôi "BoxAdaptor" trong tên nó chính là nói lên điều này: nó "adapt" (thích nghi) một "Box" widget (RenderBox) để hoạt động như một "Sliver".

Khi CustomScrollView bố cục, nó sẽ hỏi từng Sliver xem mày cần bao nhiêu không gian. Các SliverList, SliverGrid sẽ tính toán dựa trên số lượng item. Riêng SliverFillRemaining, nó sẽ chờ cho các Sliver khác tính toán xong xuôi, rồi nó mới "xem xét" còn bao nhiêu không gian trống trên màn hình (viewport) và "chiếm trọn" phần đó. Đây là lý do tại sao nó luôn nằm ở cuối và lấp đầy.

5. Ví dụ thực tế các ứng dụng/website đã ứng dụng

Anh em thấy SliverFillRemainingBoxAdaptor ở đâu ngoài đời không? Có chứ, nhiều là đằng khác!

  • Màn hình chat: Tưởng tượng một ứng dụng chat. Các tin nhắn là một danh sách cuộn. Nhưng cái thanh nhập liệu (input field) với nút gửi tin nhắn thì luôn muốn nằm sát dưới cùng màn hình, dù danh sách tin nhắn có ngắn đến đâu. Đó chính là một case hoàn hảo cho SliverFillRemainingBoxAdaptor.
  • Trang chi tiết sản phẩm: Một trang sản phẩm có ảnh, mô tả, giá cả,... và ở cuối cùng là nút "Thêm vào giỏ hàng" hoặc "Mua ngay". Nếu mô tả sản phẩm quá ngắn, tụi em không muốn cái nút đó lơ lửng giữa màn hình, mà nó phải "dính" vào cuối vùng cuộn. SliverFillRemainingBoxAdaptor làm được điều đó.
  • Các form dài: Đôi khi tụi em có một form đăng ký hay điền thông tin dài ngoằng. Cái nút "Gửi" (Submit) luôn cần nằm ở cuối form, và nếu form đó không đủ dài, nó vẫn phải dính vào đáy màn hình. SliverFillRemainingBoxAdaptor lại phát huy tác dụng.

6. Thử nghiệm đã từng và hướng dẫn nên dùng cho case nào

Anh Creyt đã từng "vật lộn" với việc làm sao để một cái nút "Add to Cart" luôn dính vào cuối màn hình trên một trang sản phẩm có nội dung động. Ban đầu, anh thử dùng Column với Expanded, nhưng khi nội dung dài ra thì cái nút đó lại bị đẩy ra ngoài vùng nhìn. Dùng Stack thì lại phức tạp khi muốn nó cuộn cùng với nội dung.

Cuối cùng, SliverFillRemainingBoxAdaptor chính là "chân ái". Anh chỉ việc đặt các Sliver chứa nội dung sản phẩm lên trên, và cuối cùng là SliverFillRemaining bọc cái nút "Add to Cart". Đảm bảo nó luôn ở đúng vị trí!

Nên dùng SliverFillRemainingBoxAdaptor khi nào?

  • Khi tụi em muốn một widget luôn luôn chiếm trọn phần không gian còn lại của CustomScrollView (từ vị trí của nó đến cuối viewport).
  • Khi tụi em cần một "footer" hay một "action bar" dính chặt vào cuối của một vùng cuộn, bất kể nội dung bên trên nó dài hay ngắn.
  • Khi tụi em đang xây dựng một UI mà cần sự linh hoạt trong việc lấp đầy không gian còn trống một cách tự động.

Không nên dùng khi nào?

  • Nếu tụi em muốn một widget có chiều cao cố định và không thay đổi. Khi đó SliverToBoxAdapter là lựa chọn tốt hơn.
  • Nếu tụi em không cần các tính năng của CustomScrollView và chỉ cần một danh sách đơn giản, ListView hoặc Column với Expanded (nếu không cuộn) sẽ đơn giản hơn.

Nhớ nhé anh em, SliverFillRemainingBoxAdaptor không chỉ là một cái tên dài, nó là một công cụ cực kỳ mạnh mẽ để tạo ra các layout cuộn linh hoạt và đẹp mắt trong Flutter. Cứ thử nghiệm đi, có gì khó cứ hỏi anh Creyt!

Thuộc Series: Flutter

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!