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 决定,到现在直接由外部函数判断。

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

相关推荐
shankss2 小时前
Flutter 下拉刷新库 pull_to_refresh_plus 设计与实现分析
flutter
忆江南19 小时前
iOS 深度解析
flutter·ios
明君8799719 小时前
Flutter 实现 AI 聊天页面 —— 记一次 Markdown 数学公式显示的踩坑之旅
前端·flutter
恋猫de小郭20 小时前
移动端开发稳了?AI 目前还无法取代客户端开发,小红书的论文告诉你数据
前端·flutter·ai编程
MakeZero1 天前
Flutter那些事-交互式组件
flutter
shankss1 天前
pull_to_refresh_simple
flutter
shankss1 天前
Flutter 下拉刷新库新特性:智能预加载 (enableSmartPreload) 详解
flutter
SoaringHeart3 天前
Flutter调试组件:打印任意组件尺寸位置信息 NRenderBox
前端·flutter
九狼3 天前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
_squirrel3 天前
记录一次 Flutter 升级遇到的问题
flutter