scroll_notification_observer.0.dart 4.47 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';

/// Flutter code sample for [ScrollNotificationObserver].

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(useMaterial3: true),
      // The Scaffold widget contains a [ScrollNotificationObserver].
      // This is used by [AppBar] for its scrolled under behavior.
      //
      // We can use [ScrollNotificationObserver.maybeOf] to get the
      // state of this [ScrollNotificationObserver] from descendants
      // of the Scaffold widget.
      //
      // If you're not using a [Scaffold] widget, you can create a  [ScrollNotificationObserver]
      // to notify its descendants of scroll notifications by adding it to the subtree.
      home: Scaffold(
        appBar: AppBar(
          title: const Text('ScrollNotificationObserver Sample'),
        ),
        body: const ScrollNotificationObserverExample(),
      ),
    );
  }
}

class ScrollNotificationObserverExample extends StatefulWidget {
  const ScrollNotificationObserverExample({super.key});

  @override
  State<ScrollNotificationObserverExample> createState() => _ScrollNotificationObserverExampleState();
}

class _ScrollNotificationObserverExampleState extends State<ScrollNotificationObserverExample> {
  ScrollNotificationObserverState? _scrollNotificationObserver;
  ScrollController controller = ScrollController();
  bool _scrolledDown = false;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // Remove any previous listener.
    _scrollNotificationObserver?.removeListener(_handleScrollNotification);
    // Get the ScrollNotificationObserverState from the Scaffold widget.
    _scrollNotificationObserver = ScrollNotificationObserver.maybeOf(context);
    // Add a new listener.
    _scrollNotificationObserver?.addListener(_handleScrollNotification);
  }

  @override
  void dispose() {
    if (_scrollNotificationObserver != null) {
      _scrollNotificationObserver!.removeListener(_handleScrollNotification);
      _scrollNotificationObserver = null;
    }
    controller.dispose();
    super.dispose();
  }

  void _handleScrollNotification(ScrollNotification notification) {
    // Check if the notification is a scroll update notification and if the
    // `notification.depth` is 0. This way we only listen to the scroll
    // notifications from the closest scrollable, instead of those that may be nested.
    if (notification is ScrollUpdateNotification && defaultScrollNotificationPredicate(notification)) {
      final ScrollMetrics metrics = notification.metrics;
      // Check if the user scrolled down.
      if (_scrolledDown != metrics.extentBefore > 0) {
        setState(() {
          _scrolledDown = metrics.extentBefore > 0;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        SampleList(controller: controller),
        // Show the button only if the user scrolled down.
        if (_scrolledDown)
          Positioned(
            right: 25,
            bottom: 20,
            child: Center(
              child: GestureDetector(
                onTap: () {
                  // Scroll to the top when the user taps the button.
                  controller.animateTo(0, duration: const Duration(milliseconds: 200), curve:Curves.fastOutSlowIn);
                },
                child: const Card(
                  child: Padding(
                    padding: EdgeInsets.all(8.0),
                    child: Column(
                      children: <Widget>[
                        Icon(Icons.arrow_upward_rounded),
                        Text('Scroll to top')
                      ],
                    ),
                  ),
                ),
              ),
            ),
          ),
      ],
    );
  }
}

class SampleList extends StatelessWidget {
  const SampleList({super.key, required this.controller});

  final ScrollController controller;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: controller,
      itemCount: 30,
      itemBuilder: (BuildContext context, int index) {
        return ListTile(title: Text('Item $index'));
      },
    );
  }
}