Flutter 告诉哪个小部件触发了 ScrollNotification?

Rya*_*ner 2 flutter

我有一个 SingleChildScrollView,它包含一个可滚动的 TabBar,并且我有一个监听通知的 NotificationListener。

我的问题是我从两个小部件都收到 ScrollNotification,但我不知道如何判断两个小部件中的哪一个触发了通知。

ScrollNotification 包含 BuildContext?上面的评论表明这可以用来确定源,但我不明白如何使用构建上下文来确定我的哪个对象触发了通知。

/// The build context of the widget that fired this notification.
///
/// This can be used to find the scrollable's render objects to determine the
/// size of the viewport, for instance.
final BuildContext? context;
Run Code Online (Sandbox Code Playgroud)

use*_*613 5

是的,BuildContext可以用于此目的。您可以用来findAncestorWidgetOfExactType查找触发小部件是否具有特定的祖先小部件类型。

例如,我有一个屏幕,有 3 个可滚动的东西: a ListView、 aGridView和水平滚动SingleChildScrollView,如下所示:

演示图片

在 中NotificationListener,我们可以检查事件的来源,如下所示:

// Cast the `BuildContext` into an `Element`
final element = (notification.context as Element);
// Try to see if the `Element` has an ancestor of type `ListView`, `GridView` or `SingleChildScrollView`.
final listView = element.findAncestorWidgetOfExactType<ListView>();
final gridView = element.findAncestorWidgetOfExactType<GridView>();
final scrollView = element.findAncestorWidgetOfExactType<SingleChildScrollView>();
// One of them should have a non-null value, the other two would be null.
print('listView: $listView');
print('gridView: $gridView');
print('scrollView: $scrollView');
Run Code Online (Sandbox Code Playgroud)

完整示例:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: 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({Key? key, required this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(backgroundColor: Colors.black),
      body: NotificationListener(
        onNotification: (notification) {
          if (notification is ScrollUpdateNotification) {
            final element = (notification.context as Element);
            final listView = element.findAncestorWidgetOfExactType<ListView>();
            final gridView = element.findAncestorWidgetOfExactType<GridView>();
            final scrollView =
                element.findAncestorWidgetOfExactType<SingleChildScrollView>();
            print('listView: $listView');
            print('gridView: $gridView');
            print('scrollView: $scrollView');
          }
          return false;
        },
        child: Column(
          children: [
            Expanded(
              child: ListView.builder(
                itemBuilder: (context, index) => ListTile(
                  title: Text('List View Item $index'),
                ),
              ),
            ),
            Expanded(
              child: GridView.builder(
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                ),
                itemBuilder: (context, index) => Center(
                  child: Text('Grid View Item $index'),
                ),
              ),
            ),
            Expanded(
              child: SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: Row(
                  children: [
                    for (int i = 0; i < 100; i++) FlutterLogo(),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)