You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter单选Checkbox实现优化方案求助

优化Flutter Checkbox单选功能的解决方案

原代码存在的问题

  • 全局变量count0、count1导致状态管理混乱,易引发不可预期的状态错误
  • onChanged和onTap中重复编写大量逻辑代码,增加维护成本与出错概率
  • 多处出现赋值错误(使用==而非=),导致状态更新失效,进而引发卡顿或崩溃
  • 直接操作全局数组的isSelected属性,状态变更缺乏统一管理

优化方案

使用单个状态变量记录选中项的索引,替代复杂的条件判断与全局计数器,统一处理单选逻辑:

import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:image_picker/image_picker.dart';
import 'package:loviser/modules/post/free_post/create_free_post.dart';
import 'package:loviser/pages/job_type_problem_page.dart';
import 'package:loviser/pages/post_to_find_page.dart';
import 'package:loviser/utils/color.dart';
import 'package:loviser/utils/utils.dart';
import 'package:loviser/values/app_color.dart';

class RowContent {
  final String iconLeadingURL;
  final String content;
  final Function(BuildContext) itemFunction;

  RowContent({
    required this.iconLeadingURL,
    required this.content,
    required this.itemFunction,
  });
}

final allRowContent = [
  RowContent(
    iconLeadingURL: 'assets/images/free.png',
    content: 'Đăng bài nhờ cộng đồng hỗ trợ miễn phí',
    itemFunction: (ctxRoot) {
      print('chose to post free');
    },
  ),
  RowContent(
    iconLeadingURL: 'assets/images/PosttoFindFreelancers.png',
    content: 'Đăng bài tìm freelancer tư vấn',
    itemFunction: (ctxRoot) {
      print('chose post find');
    },
  ),
];

class PostOption extends StatefulWidget {
  const PostOption({Key? key}) : super(key: key);

  @override
  State<PostOption> createState() => _PostOptionState();
}

class _PostOptionState extends State<PostOption> {
  // 用索引记录选中项,null表示未选中
  int? _selectedIndex;

  // 处理选中逻辑的统一方法
  void _handleSelect(int index) {
    setState(() {
      // 点击已选中项时取消选中,否则切换选中项
      _selectedIndex = _selectedIndex == index ? null : index;
      // 执行对应项的回调
      allRowContent[index].itemFunction(context);
    });
  }

  Widget itemSwitch(RowContent item, Widget flutterSwitch) {
    return ListTile(
      tileColor: Colors.white,
      shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(10))),
      leading: Image(image: AssetImage(item.iconLeadingURL)),
      title: Text(
        item.content,
        style: const TextStyle(
            fontFamily: 'AvertaStdCY-Semibold',
            fontSize: 12,
            fontWeight: FontWeight.w400),
      ),
      trailing: Container(
          margin: const EdgeInsets.fromLTRB(24, 0, 0, 0),
          height: 30,
          width: 30,
          child: FittedBox(child: flutterSwitch)),
    );
  }

  @override
  Widget build(BuildContext context) {
    final mediaQuery = MediaQuery.of(context);
    final isLandscape = mediaQuery.orientation == Orientation.landscape;
    final bool keyboardIsOpen = MediaQuery.of(context).viewInsets.bottom != 0;
    final PreferredSizeWidget appBar = AppBar(
        elevation: 0.0,
        backgroundColor: const Color(0xFFF9F9F9),
        leading: Container(
          margin: const EdgeInsets.fromLTRB(24, 0, 0, 0),
          child: IconButton(
            padding: const EdgeInsets.fromLTRB(0, 8, 8, 8),
            alignment: Alignment.center,
            onPressed: () {
              print('Exit Job Type Freelancer page');
              Navigator.pop(context);
            },
            icon: const Image(image: AssetImage('assets/images/close.png')),
          ),
        ));

    return Scaffold(
      backgroundColor: const Color(0xFFFFAFAFD),
      appBar: appBar,
      body: SingleChildScrollView(
        padding: const EdgeInsets.symmetric(horizontal: 20),
        child: Column(
            children: [
              Center(
                child: Container(
                  child: Column(
                    children: const <Widget>[
                      Text(
                        'LOVISER',
                        style: TextStyle(
                            color: Color(0xFF356899),
                            fontSize: 22,
                            fontWeight: FontWeight.bold),
                      ),
                      SizedBox(height: 7),
                      Padding(
                        padding: EdgeInsets.symmetric(horizontal: 20),
                        child: Text(
                          'Chọn giải pháp để tư vấn về vấn đề tình cảm của bạn!',
                          style: TextStyle(
                              color: Color(0xFF0D0D26),
                              fontSize: 20,
                              fontWeight: FontWeight.bold),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 10),
              for (int i = 0; i < allRowContent.length; i++) ...[
                const SizedBox(height: 7),
                Column(
                  children: [
                    ListTile(
                      tileColor: Colors.white,
                      shape: const RoundedRectangleBorder(
                          borderRadius: BorderRadius.all(Radius.circular(10))),
                      leading: Image(
                          width: 32,
                          height: 31,
                          image: AssetImage(allRowContent[i].iconLeadingURL)),
                      title: Text(
                        allRowContent[i].content,
                        style: const TextStyle(
                            color: Color(0xff0D0D26),
                            fontFamily: 'AvertaStdCY-Semibold',
                            fontSize: 15,
                            fontWeight: FontWeight.w100),
                      ),
                      trailing: Checkbox(
                        activeColor: Colors.red,
                        shape: const CircleBorder(),
                        side: const BorderSide(
                            width: 1, color: Color(0xFFCACBCE)),
                        value: _selectedIndex == i,
                        onChanged: (value) {
                          _handleSelect(i);
                        },
                      ),
                      onTap: () {
                        _handleSelect(i);
                      },
                    ),
                  ],
                ),
              ],
              const SizedBox(height: 10),
              const SizedBox(height: 10),
              Padding(
                padding: const EdgeInsets.fromLTRB(0, 20, 0, 0),
                child: Align(
                  alignment: Alignment.bottomCenter,
                  child: TextButton(
                    style: TextButton.styleFrom(
                      backgroundColor: const Color(0xffEC1C24),
                      minimumSize: const Size(327, 56),
                    ),
                    onPressed: () => {
                      if (_selectedIndex == 0) {
                        _showModalBottomSheetForFreePost()
                      } else if (_selectedIndex == 1) {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                              builder: (context) => PostToFind()),
                        )
                      }
                    },
                    child: const Text(
                      'Tiếp tục',
                      style: TextStyle(
                        fontFamily: 'Poppins-Regular',
                        fontSize: 16,
                        color: Color(0xffffffff),
                      ),
                    ),
                  ),
                ),
              ),
            ]),
      ),
    );
  }

  void _showModalBottomSheetForFeePost() {
    showModalBottomSheet(
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(30),
          topRight: Radius.circular(30),
        ),
      ),
      context: context,
      builder: (builder) {
        final width = MediaQuery.of(context).size.width;
        final height = MediaQuery.of(context).size.height;
        return Container(
          decoration: const BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(30.0),
              topRight: Radius.circular(100.0),
            ),
          ),
          height: height * 0.32,
          child: Column(
            children: [
              const Icon(Icons.remove, size: 40),
              SizedBox(
                width: width * 0.8,
                child: const Text(
                  "Bạn đang gặp vấn đề về chuyện tình cảm? xxx",
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    color: Color(0xFF150B3D),
                    fontWeight: FontWeight.bold,
                    fontSize: 24,
                  ),
                ),
              ),
              SizedBox(
                height: height * 0.01,
              ),
              SizedBox(
                width: width * 0.6,
                child: const Text(
                  "Đăng vấn đề của bạn và nhờ một freelancer có kinh nghiệm tư vấn!",
                  textAlign: TextAlign.center,
                ),
              ),
              SizedBox(
                height: height * 0.05,
              ),
              SizedBox(
                height: 50,
                width: 315,
                child: ElevatedButton(
                  onPressed: () {
                    Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (_) => const PostOption(),
                      ),
                    );
                  },
                  style: ElevatedButton.styleFrom(
                    primary: Colors.red,
                  ),
                  child: const Text(
                    "Đăng vấn đề",
                    style: TextStyle(
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  void _showModalBottomSheetForFreePost() {
    print('222');
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) => const CreateFreePost(),
      ),
    );
  }
}

优化说明

  • 移除全局计数器与isSelected属性,改用_selectedIndex状态变量统一管理选中状态,逻辑更清晰
  • 封装_handleSelect方法,统一处理onChanged与onTap事件,消除代码冗余
  • 修复原代码中==赋值错误的问题,确保状态更新正确
  • 简化Checkbox渲染逻辑,无需根据选中状态拆分组件,提升渲染性能与代码可读性

内容的提问来源于stack exchange,提问作者Trong Nhan Nguyen Le

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 20:00:47