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

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

相关推荐
2501_946244782 小时前
Flutter & OpenHarmony OA系统设置页面组件开发指南
开发语言·javascript·flutter
l134062082358 小时前
Flutter Geocoding 在鸿蒙上的使用指南
flutter·华为·harmonyos
AiFlutter9 小时前
三、内容展示(02):图片
flutter·低代码·低代码平台·aiflutter·aiflutter低代码
l1340620823510 小时前
344.在鸿蒙上使用 animations Flutter 包的指南
flutter·华为·harmonyos
2501_9462447811 小时前
Flutter & OpenHarmony OA系统底部导航栏组件开发指南
android·javascript·flutter
2501_9444460011 小时前
Flutter&OpenHarmony字体与排版设计
android·javascript·flutter
消失的旧时光-194311 小时前
mixin 写一个 Flutter 的“埋点 + 日志 + 性能监控”完整框架示例
android·flutter
消失的旧时光-194312 小时前
Flutter 工程中 mixin 的正确打开方式:5 种高质量设计范式 + mixin vs 继承 vs 组合 + 为什么它比 BasePage 更优雅
前端·flutter·架构
消失的旧时光-194313 小时前
Flutter 中 mixin 的完整认知体系——从原理、范式、架构选择到反模式(工程实战版)
flutter·架构
2501_9462447813 小时前
Flutter & OpenHarmony OA系统下拉刷新组件开发指南
开发语言·javascript·flutter