Tag: Widget

  • Flutter Custom Radio Button với Custom Shapes

    Sự phát triển của Flutter đang trở nên phổ biến và ngày càng phổ biến hơn do có nhiều tùy biến, widget tùy chỉnh và cách tiếp cận rất dễ thực hiện. Hôm nay chúng ta sẽ tìm hiểu cách tạo nút radio tùy chỉnh Flutter với các hình dạng tùy chỉnh như hộp đựng hình vuông, hộp đựng hình tròn hoặc biểu tượng. Tôi đã cố gắng tạo ra các phương pháp khác nhau để chúng có thể được sử dụng theo nhu cầu và nhà phát triển sẽ học từ hướng dẫn này cũng có thể có ý tưởng tạo nút radio tùy chỉnh rung của riêng mình, với hình dạng hoặc tiện ích theo yêu cầu của mình.

    Ví dụ về Nút radio tùy chỉnh Flutter này sẽ tăng tốc độ phát triển của bạn và tiết kiệm thời gian của bạn bằng cách gọi nó trực tiếp như một phần tử con của bất kỳ tiện ích nào hoặc bằng cách thực hiện các phương thức tĩnh khi nó cần thiết. Bởi vì không có ứng dụng nào có thể được thực hiện mà không thực hiện bất kỳ widegt tùy chỉnh nào, tất cả các widget được tạo sẵn không phải lúc nào cũng giúp ích được. Vì vậy, học cách chế tạo widget tùy chỉnh là phần quan trọng nhất của quá trình phát triển Flagship.

    Hãy bắt đầu thực hiện từng bước để chúng ta có thể hiểu những gì cần phải làm để đạt được kết quả đầu ra mong muốn. Đầu tiên, chúng ta sẽ tạo widget home, nơi chúng ta sẽ hiển thị các nút radio tùy chỉnh.

    Nó là một widget toàn diện có khả năng xử lý tất cả các chức năng của nút radio, như hiển thị thiết kế tùy chỉnh, sự kiện chạm và lựa chọn, v.v.

    home_screen.dart

    class HomeScreen extends StatefulWidget {
      const HomeScreen({Key? key}) : super(key: key);
      @override
      _HomeScreenState createState() => _HomeScreenState();
    }
    class _HomeScreenState extends State {
      List radioOptions = [];
      @override
      void initState() {
        super.initState();
        radioOptions.add(RadioButtonModel(false, 'A', 'Option A'));
        radioOptions.add(RadioButtonModel(false, 'B', 'Option B'));
        radioOptions.add(RadioButtonModel(false, 'C', 'Option C'));
        radioOptions.add(RadioButtonModel(false, 'D', 'Option D'));
      }
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Custom Check Box Example'),
          ),
          body: MyRadioButtonWidget(
            options: radioOptions,
            onItemSelected: (index) {
              print('I am index: $index');
            },
          ),
        );
      }
    }

    Giờ hãy tạo nút radio tùy chỉnh

    Lớp này chịu trách nhiệm xử lý tất cả các hoạt động của nút radio tùy chỉnh, như thiết kế, sự kiện và gọi lại. Nếu bạn nhận thấy, tôi đã thêm một chức năng gọi lại để trả về chỉ mục mục được nhấn hiện tại. Trong trường hợp bạn muốn làm cho lớp nút radio tùy chỉnh của mình độc lập và chung chung, bạn có thể tạo cho các sự kiện theo yêu cầu của mình.

    final Function(int)? onItemSelected;

    my_radio_button_widget.dart

    class MyRadioButtonWidget extends StatefulWidget {
      final List<RadioButtonModel>? options;
      final Function(int)? onItemSelected;
      const MyRadioButtonWidget({Key? key, this.options, this.onItemSelected})
          : super(key: key);
      @override
      createState() {
        return MyRadioButtonWidgetState();
      }
    }
    class MyRadioButtonWidgetState extends State<MyRadioButtonWidget> {
      @override
      Widget build(BuildContext context) {
        return ListView.builder(
          itemCount: widget.options!.length,
          itemBuilder: (BuildContext context, int index) {
            return InkWell(
              onTap: () {
                setState(() {
                  for (var element in widget.options!) {
                    element.isSelected = false;
                  }
                  widget.options![index].isSelected = true;
                  widget.onItemSelected!(index);
                });
              },
              child: CircleRadioButtonItem(widget.options![index]),
            );
          },
        );
      }
    }

    Radio Button Model

    class RadioButtonModel {
      bool isSelected;
      final String buttonText;
      final String text;
      RadioButtonModel(this.isSelected, this.buttonText, this.text);
    }

    Bây giờ chúng tôi sẽ tạo các hình dạng tùy chỉnh cho nút radio của chúng ta, ta có nhiều tùy chọn, bạn có thể tìm thấy ba lớp khác nhau để thể hiện phong cách khác nhau. bằng cách sử dụng ý tưởng này, bạn có thể tự tạo và sửa đổi các ví dụ đã cho theo ý muốn.

    Nút radio vuông

    Ở đây chúng tôi đang tạo hộp chứa hình vuông với văn bản bên trong nó.

    class SquareRadioButtonItem extends StatelessWidget {
      final RadioButtonModel _item;
      const SquareRadioButtonItem(this._item, {Key? key}) : super(key: key);
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: const EdgeInsets.all(15.0),
          child: Row(
            mainAxisSize: MainAxisSize.max,
            children: [
              Container(
                height: 50.0,
                width: 50.0,
                child: Center(
                  child: Text(_item.buttonText,
                      style: TextStyle(
                          color: _item.isSelected ? Colors.white : Colors.black,
                          //fontWeight: FontWeight.bold,
                          fontSize: 18.0)),
                ),
                decoration: BoxDecoration(
                  color: _item.isSelected ? Colors.blueAccent : Colors.transparent,
                  border: Border.all(
                      width: 1.0,
                      color: _item.isSelected ? Colors.blueAccent : Colors.grey),
                  borderRadius: const BorderRadius.all(Radius.circular(2.0)),
                ),
              ),
              Container(
                margin: const EdgeInsets.only(left: 10.0),
                child: Text(_item.text),
              )
            ],
          ),
        );
      }
    }

    Nút radio tròn

    class CircleRadioButtonItem extends StatelessWidget {
      final RadioButtonModel _item;
      const CircleRadioButtonItem(this._item, {Key? key}) : super(key: key);
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: const EdgeInsets.all(15.0),
          child: Row(
            mainAxisSize: MainAxisSize.max,
            children: [
              Container(
                height: 50.0,
                width: 50.0,
                child: Center(
                  child: Text(_item.buttonText,
                      style: TextStyle(
                          color: _item.isSelected ? Colors.white : Colors.black,
                          //fontWeight: FontWeight.bold,
                          fontSize: 18.0)),
                ),
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: _item.isSelected ? Colors.blueAccent : Colors.transparent,
                  border: Border.all(
                      width: 1.0,
                      color: _item.isSelected ? Colors.blueAccent : Colors.grey),
                ),
              ),
              Container(
                margin: const EdgeInsets.only(left: 10.0),
                child: Text(_item.text),
              )
            ],
          ),
        );
      }
    }

    Icon Radio

    class IconRadioButtonItem extends StatelessWidget {
      final RadioButtonModel _item;
      const IconRadioButtonItem(this._item, {Key? key}) : super(key: key);
      @override
      Widget build(BuildContext context) {
        return Container(
          margin: const EdgeInsets.all(15.0),
          child: Row(
            mainAxisSize: MainAxisSize.max,
            children: [
              _item.isSelected
                  ? const Icon(Icons.circle)
                  : const Icon(Icons.circle_outlined),
              Container(
                margin: const EdgeInsets.only(left: 10.0),
                child: Text(_item.text),
              )
            ],
          ),
        );
      }
    }

  • [Flutter] Bố cục Layout (Phần 2)

    [Flutter] Bố cục Layout (Phần 2)

    Tiếp tục phần trước, ta sẽ tiếp tục tìm hiểu thêm các widget giúp điều chỉnh bố cục hiển thị.

    IntrinsicWidth and IntrinsicHeight

    Tất cả tiện ích con bên trong Hàng hoặc Cột có chiều cao/rộng bằng tiện ích con cao nhất/rộng nhất

    Trong trường hợp bạn có kiểu bố trí này:

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('IntrinsicWidth')),
        body: Center(
          child: Column(
            children: <Widget>[
              RaisedButton(
                onPressed: () {},
                child: Text('Short'),
              ),
              RaisedButton(
                onPressed: () {},
                child: Text('A bit Longer'),
              ),
              RaisedButton(
                onPressed: () {},
                child: Text('The Longest text button'),
              ),
            ],
          ),
        ),
      );
    }

    Nhưng bạn muốn có tất cả các nút theo nút rộng nhất , chỉ cần sử dụng :IntrinsicWidth

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('IntrinsicWidth')),
        body: Center(
          child: IntrinsicWidth(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                RaisedButton(
                  onPressed: () {},
                  child: Text('Short'),
                ),
                RaisedButton(
                  onPressed: () {},
                  child: Text('A bit Longer'),
                ),
                RaisedButton(
                  onPressed: () {},
                  child: Text('The Longest text button'),
                ),
              ],
            ),
          ),
        ),
      );
    }

    Trong trường hợp bạn gặp vấn đề tương tự nhưng bạn muốn có tất cả các widget có chiều cao theo widget cao nhất chỉ cần sử dụng kết hợp của IntrinsicHeight và Row .

    IntrinsicWidthIntrinsicHeight còn được sử dụng để định kích thước con của nó theo chiều rộng/cao nội tại tối đa của nó. Nó có thể hữu ích nếu chiều rộng/cao khả dụng là không giới hạn, nhưng bạn muốn đặt kích thước của tiện ích con thành chiều rộng nội tại của nó.

    . . .

    Stack

    Chồng các Widget lên nhau

    @override
    Widget build(BuildContext context) {
      Widget main = Scaffold(
        appBar: AppBar(title: Text('Stack')),
      );
    
      return Stack(
        fit: StackFit.expand,
        children: <Widget>[
          main,
          Banner(
            message: "Top Start",
            location: BannerLocation.topStart,
          ),
          Banner(
            message: "Top End",
            location: BannerLocation.topEnd,
          ),
          Banner(
            message: "Bottom Start",
            location: BannerLocation.bottomStart,
          ),
          Banner(
            message: "Bottom End",
            location: BannerLocation.bottomEnd,
          ),
        ],
      );
    }

    . . .

    Bạn có thể các widget của mình vào Positioned Widget để xác định vị trí hiện thị

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('Stack')),
        body: Stack(
          fit: StackFit.expand,
          children: <Widget>[
            Material(color: Colors.yellowAccent),
            Positioned(
              top: 0,
              left: 0,
              child: Icon(Icons.star, size: 50),
            ),
            Positioned(
              top: 340,
              left: 250,
              child: Icon(Icons.call, size: 50),
            ),
          ],
        ),
      );
    }

    . . .

    Nếu bạn không muốn đoán các giá trị trên cùng/dưới cùng, bạn có thể sử dụng LayoutBuilder để lấy chúng

    Widget build(BuildContext context) {
      const iconSize = 50;
      return Scaffold(
        appBar: AppBar(title: Text('Stack with LayoutBuilder')),
        body: LayoutBuilder(
          builder: (context, constraints) =>
            Stack(
              fit: StackFit.expand,
              children: <Widget>[
                Material(color: Colors.yellowAccent),
                Positioned(
                  top: 0,
                  child: Icon(Icons.star, size: iconSize),
                ),
                Positioned(
                  top: constraints.maxHeight - iconSize,
                  left: constraints.maxWidth - iconSize,
                  child: Icon(Icons.call, size: iconSize),
                ),
              ],
            ),
        ),
      );
    }

    . . .

    Expanded

    Expanded hoạt động với bố cục Flex\Flexbox và rất tốt để phân phối không gian giữa nhiều mục.

    Row(
      children: <Widget>[
        Expanded(
          child: Container(
            decoration: const BoxDecoration(color: Colors.red),
          ),
          flex: 3,
        ),
        Expanded(
          child: Container(
            decoration: const BoxDecoration(color: Colors.green),
          ),
          flex: 2,
        ),
        Expanded(
          child: Container(
            decoration: const BoxDecoration(color: Colors.blue),
          ),
          flex: 1,
        ),
      ],
    ),

    . . .

    ConstrainedBox

    Theo mặc định, hầu hết các tiện ích con sẽ sử dụng ít dung lượng nhất có thể:

    Card(child: const Text('Hello World!'), color: Colors.yellow)

    . . .

    ConstrainedBox cho phép tiện ích con sử dụng không gian còn lại như mong muốn.

    ConstrainedBox( 
      constraints: BoxConstraints.expand(),
      child: const Card(
        child: const Text('Hello World!'), 
        color: Colors.yellow,
      ), 
    ),

    . . .

    Bằng cách sử dụng BoxConstraints, bạn chỉ định lượng không gian mà một widget có thể có – bạn chỉ định minmax của heightwidth.

    BoxConstraints.expand sử dụng lượng không gian vô hạn (tất cả khả dụng) trừ khi được chỉ định:

    ConstrainedBox(
      constraints: BoxConstraints.expand(height: 300),
      child: const Card(
        child: const Text('Hello World!'), 
        color: Colors.yellow,
      ),
    ),

    Và nó giống như:

    ConstrainedBox(
      constraints: BoxConstraints(
        minWidth: double.infinity,
        maxWidth: double.infinity,
        minHeight: 300,
        maxHeight: 300,
      ),
      child: const Card(
        child: const Text('Hello World!'), 
        color: Colors.yellow,
      ),
    ),

    . . .

    Align

    Đôi khi bạn gặp khó khăn trong việc đặt tiện ích con của chúng tôi ở kích thước phù hợp – ví dụ: nó liên tục bị kéo căng khi bạn không muốn:

    Ví dụ ở trên xảy ra khi bạn có Column với CrossAxisAlignment.stretch và bạn chỉ muốn nút không bị kéo căng:

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(title: Text('Align: without Align')),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Align(
              child: RaisedButton(
                onPressed: () {},
                child: const Text('Button'),
              ),
            ),
          ],
        ),
      );
    }

    Khi tiện ích con của bạn không tuân theo các ràng buộc mà bạn cố gắng thiết lập, trước tiên hãy thử kết hợp với nó Align

    Kết thúc phần 2!

    . . .

  • [Flutter] Bố cục Layout (Phần 1)

    [Flutter] Bố cục Layout (Phần 1)

    Bạn cần các mẫu bố cục đơn giản cho Flutter?
    Tôi giới thiệu cho bạn tập hợp các đoạn mã bố cục Flutter của tôi. Tôi sẽ giữ cho nó ngắn gọn, dễ hiểu và đơn giản với vô số ví dụ trực quan.

    Row và Column

    MainAxisAlignment

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.start,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.end,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    CrossAxisAlignment

    Bạn nên sử dụng CrossAxisAlignment.baseline nếu bạn yêu cầu căn chỉnh đường cơ sở của các văn bản khác nhau

    Row(
      crossAxisAlignment: CrossAxisAlignment.baseline,
      textBaseline: TextBaseline.alphabetic,
      children: <Widget>[
        Text(
          'Baseline',
          style: Theme.of(context).textTheme.display3,
        ),
        Text(
          'Baseline',
          style: Theme.of(context).textTheme.body1,
        ),
      ],
    ),

    . . .

    Row /*or Column*/( 
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 200),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 200),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      crossAxisAlignment: CrossAxisAlignment.end,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 200),
        Icon(Icons.star, size: 50),
      ],
    ),

    . . .

    Row /*or Column*/( 
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        Icon(Icons.star, size: 50),
        Icon(Icons.star, size: 200),
        Icon(Icons.star, size: 50),
      ],
    ),

    Kết thúc phần 1!