flutter 如何使用`GlobalObjectKey`的滚动到某些小部件?现在他们抛出“重复的GlobalKeys”异常

deikduxw  于 2023-04-13  发布在  Flutter
关注(0)|答案(1)|浏览(184)

我有一个屏幕上的小部件列表,滚动到点击选择的元素.为了实现这一点,我使用Scrollable.ensureVisible和GlobalObjectKeys.它通常工作良好,除了一种情况.如果我在我的滚动屏幕上,并通过Navigator.pushReplacement再次推这个屏幕发生“Duplicate GlobalKeys detected in widget tree”错误(如果我按下其他屏幕,然后滚动屏幕-一切都没问题)。但一切都很好-应用程序没有崩溃,滚动按预期工作。无法找到解决此问题的方法。
最小可重现示例(只需点击FAB即可重现问题):

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final ScrollController _chipsScrollController = ScrollController();
  String _currentChip = 'Chip 1';
  static const _chips = [
    'Chip 1',
    'Chip 2',
    'Chip 3',
    'Chip 4',
    'Chip 5',
    'Chip 6',
    'Chip 7',
    'Chip 8',
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: SizedBox(
          height: 70,
          child: ListView.separated(
            controller: _chipsScrollController,
            physics: const ClampingScrollPhysics(),
            padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
            scrollDirection: Axis.horizontal,
            itemCount: _chips.length,
            itemBuilder: (final BuildContext context, final int index) {
              return ChoiceChip(
                  key: GlobalObjectKey(_chips[index]),
                  label: Text(_chips[index]),
                  selected: _chips[index] == _currentChip,
                  onSelected: (final bool value) {
                    _currentChip = _chips[index];
                    Scrollable.ensureVisible(
                      GlobalObjectKey(_chips[index]).currentContext!,
                      alignment: 0.5,
                    );
                  });
            },
            separatorBuilder: (final BuildContext context, final int index) =>
                const SizedBox(width: 12),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => Navigator.pushReplacement(
          context,
          MaterialPageRoute(
            builder: (_) => const MyHomePage(
              title: 'Flutter Demo Home Page',
            ),
          ),
        ),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
shyt4zoc

shyt4zoc1#

如果你真的需要这样做(请参阅@YeasinSheikh的评论),你需要确保这些全局键在你的应用程序中是唯一的。要做到这一点,你可以在你的MyHomePage的每个示例中添加一个GlobalKey,并将这个键与你用于Chip小部件的键结合使用。
试试这个代码:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(key: GlobalKey(), title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  // add a required Key param to the constructor and pass it to super
  const MyHomePage({required Key key, required this.title}): super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final ScrollController _chipsScrollController = ScrollController();
  String _currentChip = 'Chip 1';
  static const _chips = [
    'Chip 1',
    'Chip 2',
    'Chip 3',
    'Chip 4',
    'Chip 5',
    'Chip 6',
    'Chip 7',
    'Chip 8',
    'Chip 9',
    'Chip 10',
    'Chip 11',
    'Chip 12',
    'Chip 13',
    'Chip 14',
    'Chip 15',
    'Chip 16',
    'Chip 18',
    'Chip 19',
    'Chip 20',
    'Chip 21',
    'Chip 22',
    'Chip 23',
    'Chip 24',     
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: SizedBox(
          height: 70,
          child: ListView.separated(
            controller: _chipsScrollController,
            physics: const ClampingScrollPhysics(),
            padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
            scrollDirection: Axis.horizontal,
            itemCount: _chips.length,
            itemBuilder: (final BuildContext context, final int index) {
              return ChoiceChip(
                  // key now will be globally unique 
                  key: GlobalObjectKey('${widget.key}_${_chips[index]}'),
                  label: Text(_chips[index]),
                  selected: _chips[index] == _currentChip,
                  onSelected: (final bool value) {
                    _currentChip = _chips[index];
                    Scrollable.ensureVisible(
                      GlobalObjectKey('${widget.key}_${_chips[index]}').currentContext!,
                      alignment: 0.5,
                    );
                  });
            },
            separatorBuilder: (final BuildContext context, final int index) =>
                const SizedBox(width: 12),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => Navigator.pushReplacement(
          context,
          MaterialPageRoute(
            builder: (_) => MyHomePage(key: GlobalKey(),
              title: 'Flutter Demo Home Page',
            ),
          ),
        ),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

相关问题