Unverified Commit c05a05e6 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub
parent 3e610f73
// 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'));
},
);
}
}
// 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';
import 'package:flutter_api_samples/widgets/scroll_notification_observer/scroll_notification_observer.0.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Scroll to top buttons appears when scrolling down', (WidgetTester tester) async {
const String buttonText = 'Scroll to top';
await tester.pumpWidget(
const example.ScrollNotificationObserverApp(),
);
expect(find.byType(ScrollNotificationObserver), findsOneWidget);
expect(find.text(buttonText), findsNothing);
// Scroll down.
await tester.drag(find.byType(ListView), const Offset(0.0, -300.0));
await tester.pumpAndSettle();
expect(find.text(buttonText), findsOneWidget);
await tester.tap(find.text(buttonText));
await tester.pumpAndSettle();
expect(find.text(buttonText), findsNothing);
});
}
......@@ -71,6 +71,15 @@ final class _ListenerEntry extends LinkedListEntry<_ListenerEntry> {
/// This widget is similar to [NotificationListener]. It supports a listener
/// list instead of just a single listener and its listeners run
/// unconditionally, they do not require a gating boolean return value.
///
/// {@tool dartpad}
/// This sample shows a "Scroll to top" button that uses [ScrollNotificationObserver]
/// to listen for scroll notifications from [ListView]. The button is only visible
/// when the user has scrolled down. When pressed, the button animates the scroll
/// position of the [ListView] back to the top.
///
/// ** See code in examples/api/lib/widgets/scroll_notification_observer/scroll_notification_observer.0.dart **
/// {@end-tool}
class ScrollNotificationObserver extends StatefulWidget {
/// Create a [ScrollNotificationObserver].
///
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment