Flutter | 基于函数式编程的通用单选列表设计

背景

项目中多次用到如下图的通用单选列表页:

常规封装

此列表需要三样东西:

  1. 标题数组
  2. 当前选中项的 index
  3. 点击 cell 的回调

封装大体如下:

dart 复制代码
import 'package:flutter/material.dart';

class ListPage1 extends StatefulWidget {
  const ListPage1({
    super.key,
    required this.titles,
    required this.selectedIndex,
    required this.onSelected,
  });

  // 标题数组	
  final List<String> titles;
  // 当前选中项的 index
  final int selectedIndex;
  // 点击 cell 的回调
  final ValueChanged<int> onSelected;

  @override
  State<ListPage1> createState() => _ListPage1State();
}

class _ListPage1State extends State<ListPage1> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("ListPage1")),
      body: ListView.builder(
        itemCount: widget.titles.length,
        itemBuilder: (context, index) {
          // 根据传入的index判断是否选中
          final isSelected = widget.selectedIndex == index;
          return GestureDetector(
            onTap: () {
              Navigator.pop(context);
              widget.onSelected(index);
            },
            child: Container(
              width: double.infinity,
              color: isSelected ? Colors.orange : Colors.white,
              height: 50,
              child: Text(
                widget.titles[index],
                style: const TextStyle(fontSize: 30),
              ),
            ),
          );
        },
      ),
    );
  }
}

使用:

dart 复制代码
final person1 = Person(name: 'amy', age: 10);
final person2 = Person(name: 'bob', age: 20);
final person3 = Person(name: 'candy', age: 30);
final persons = [person1, person2, person3];
final normalList = ListPage1(
  titles: persons.map((e) => e.name).toList(),
  selectedIndex: _selectedIndex,
  onSelected: (selectIndex) {
    setState(() {
      // 保存选中的index
      _selectedIndex = selectIndex;
    });
  },
);
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => normalList),
);

存在的问题:

必须在外部保存这个 selectedIndex,但我们真正需要的其实是这个 index 对应的 person

函数式封装

目标是在功能不变的前提下消除 selectedIndex

代码如下:

dart 复制代码
import 'package:flutter/material.dart';

class ListPage2<T> extends StatefulWidget {
  const ListPage2({
    super.key,
    required this.values,
    required this.titleBuilder,
    required this.selectedStateBuilder,
    required this.onSelected,
  });

  final List<T> values;

  // 用函数创建 title
  final String Function(T) titleBuilder;
  // 用函数表示选中状态
  final bool Function(T) selectedStateBuilder;
  // 选中回调,返回 model
  final ValueChanged<T> onSelected;

  @override
  State<ListPage2<T>> createState() => _ListPage2State<T>();
}

class _ListPage2State<T> extends State<ListPage2<T>> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("ListPage2")),
      body: ListView.builder(
        itemCount: widget.values.length,
        itemBuilder: (context, index) {
          final currentItem = widget.values[index];
          final isSelected = widget.selectedStateBuilder(currentItem);
          return GestureDetector(
            onTap: () {
              Navigator.pop(context);
              widget.onSelected(currentItem);
            },
            child: Container(
              width: double.infinity,
              color: isSelected ? Colors.orange : Colors.white,
              height: 50,
              child: Text(
                widget.titleBuilder(currentItem),
                style: const TextStyle(fontSize: 30),
              ),
            ),
          );
        },
      ),
    );
  }
}

使用:

dart 复制代码
final person1 = Person(name: 'amy', age: 10);
final person2 = Person(name: 'bob', age: 20);
final person3 = Person(name: 'candy', age: 30);
final persons = [person1, person2, person3];
final listPage = ListPage2<Person>(
    values: persons,
    titleBuilder: (person) {
      return person.name;
    },
    selectedStateBuilder: (person) {
      return person.name == _currentPerson?.name;
    },
    onSelected: (person) {
      setState(() {
        _currentPerson = person;
      });
    });
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => listPage),
);

这种写法直接保存了选中的 person,没有了中间变量 selectedIndex,选中状态从之前的通过 selectedIndex 决定,到现在直接由外部函数判断。

所有功能未做任何改变,只是编程思想由命令式变成了函数式。

相关推荐
程序员老刘14 小时前
2025年Flutter状态管理新趋势:AI友好度成为技术选型第一标准
flutter·ai编程·客户端
AGG_Chan18 小时前
flutter专栏--深入了解widget原理
开发语言·javascript·flutter
墨客希20 小时前
通俗易懂的理解Vue.js
vue.js·flutter
—Qeyser21 小时前
Flutter bottomNavigationBar 底部导航栏
flutter
ZFJ_张福杰21 小时前
【Flutter】APP的数据安全(基于Flutter 交易所APP的总结)
flutter·web3·区块链·app·交易所
Bryce李小白4 天前
Flutter 自定义 View 权威指引
flutter
恋猫de小郭4 天前
Fluttercon EU 2025 :Let‘s go far with Flutter
android·开发语言·flutter·ios·golang
SoaringHeart5 天前
Flutter进阶:自定义一个 json 转 model 工具
前端·flutter·dart
许泽宇的技术分享6 天前
Flutter + Ollama:开启本地AI的全平台新纪元 —— 从零剖析一款现代化AI客户端的技术奥秘
人工智能·flutter