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

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

相关推荐
maaath39 分钟前
【maaath】Flutter for OpenHarmony 跨平台计算器应用开发实践
flutter·华为·harmonyos
maaath6 小时前
【maaath】Flutter for OpenHarmony 闹钟时钟应用开发实战
flutter·华为·harmonyos
maaath6 小时前
【maaath】Flutter for OpenHarmony 短信管理应用实战
flutter·华为·harmonyos
maaath7 小时前
【maaath】Flutter for OpenHarmony打造跨平台便签备忘录应用
flutter·华为·harmonyos
千码君20167 小时前
flutter:与Android Studio模拟器的调试分享
android·flutter
xmdy58668 小时前
Flutter+开源鸿蒙实战|智联邻里Day8 Lottie动画集成+url_launcher跳转拨号+个人中心完善+全局UI统一
flutter·开源·harmonyos
liulian091616 小时前
Flutter for OpenHarmony 跨平台开发:颜色选择器功能实战指南
flutter
liulian091620 小时前
Flutter for OpenHarmony 跨平台开发:BMI计算器功能实战指南
flutter·华为
xmdy58661 天前
Flutter+开源鸿蒙实战|智安盾电商溯源平台Day1 项目搭建与整体方案拆解
flutter·开源·harmonyos
小白64021 天前
AI辅助设计Flutter蓝牙自动连接系统
人工智能·flutter