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

Flutter中ListWheelScrollView选中项状态更新异常问题求助

问题原因分析
  • 闭包捕获旧值:onSelectedItemChanged回调里的month是MainPage构建时捕获的旧值,不会随着状态更新而变化,所以打印的始终是弹窗打开时的初始值。
  • 初始索引错误:DateTime.now().month返回1-12,但列表索引是0-11,初始状态会导致默认选中的月份偏移1位。
解决方案一:修复Riverpod实现

调整初始值,同时在回调里直接读取最新状态或者打印更新后的索引:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

const List<String> _months = <String>[
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December'
];

void main() => runApp(const ProviderScope(child: MyApp()));

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MainPage(),
    );
  }
}

// 初始值改为当前月份-1,匹配列表索引0-11
final monthIndexProvider = StateProvider<int>((ref) => DateTime.now().month - 1);

class MainPage extends ConsumerWidget {
  const MainPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final int month = ref.watch(monthIndexProvider);

    return Scaffold(
      body: Center(
        child: ElevatedButton(
          child: Text('${month + 1}'), // 显示时转成1-12的月份
          onPressed: () async {
            await showModalBottomSheet(
              context: context,
              builder: (BuildContext context) {
                return SizedBox(
                  height: 300,
                  child: ListWheelScrollView.useDelegate(
                    itemExtent: 50,
                    perspective: 0.001,
                    diameterRatio: 1.6,
                    physics: const FixedExtentScrollPhysics(),
                    squeeze: 1.0,
                    overAndUnderCenterOpacity: 0.2,
                    useMagnifier: true,
                    magnification: 1.3,
                    // 初始化选中当前状态的索引
                    controller: FixedExtentScrollController(initialItem: month),
                    onSelectedItemChanged: (index) {
                      ref.read(monthIndexProvider.notifier).state = index;
                      // 方式1:直接打印更新后的索引
                      print('选中索引:$index,对应月份:${index + 1}');
                      // 方式2:读取最新状态打印
                      // print('最新状态:${ref.read(monthIndexProvider)}');
                    },
                    childDelegate: ListWheelChildBuilderDelegate(
                      childCount: 12,
                      builder: (context, index) {
                        return Center(child: Text(_months[index]));
                      },
                    ),
                  ),
                );
              },
            );
          },
        ),
      ),
    );
  }
}

关键修改点:

  • 修正初始索引为DateTime.now().month - 1,匹配列表的0-11索引
  • 给ListWheelScrollView添加FixedExtentScrollController,确保弹窗打开时选中当前状态的月份
  • 回调里直接打印更新后的index,或者通过ref.read获取最新状态,避免使用闭包捕获的旧值
解决方案二:有状态组件实现

如果不想用Riverpod,使用StatefulWidget实现:

import 'package:flutter/material.dart';

const List<String> _months = <String>[
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December'
];

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MainPage(),
    );
  }
}

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

  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  late int _selectedMonthIndex;

  @override
  void initState() {
    super.initState();
    // 初始索引匹配列表0-11
    _selectedMonthIndex = DateTime.now().month - 1;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          child: Text('${_selectedMonthIndex + 1}'),
          onPressed: () async {
            await showModalBottomSheet(
              context: context,
              builder: (BuildContext context) {
                return SizedBox(
                  height: 300,
                  child: ListWheelScrollView.useDelegate(
                    itemExtent: 50,
                    perspective: 0.001,
                    diameterRatio: 1.6,
                    physics: const FixedExtentScrollPhysics(),
                    squeeze: 1.0,
                    overAndUnderCenterOpacity: 0.2,
                    useMagnifier: true,
                    magnification: 1.3,
                    controller: FixedExtentScrollController(initialItem: _selectedMonthIndex),
                    onSelectedItemChanged: (index) {
                      setState(() {
                        _selectedMonthIndex = index;
                      });
                      print('选中索引:$index,对应月份:${index + 1}');
                    },
                    childDelegate: ListWheelChildBuilderDelegate(
                      childCount: 12,
                      builder: (context, index) {
                        return Center(child: Text(_months[index]));
                      },
                    ),
                  ),
                );
              },
            );
          },
        ),
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Mathieu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 11:11:15