diff --git a/examples/api/lib/widgets/page_view/page_view.1.dart b/examples/api/lib/widgets/page_view/page_view.1.dart new file mode 100644 index 0000000000..56aef059a3 --- /dev/null +++ b/examples/api/lib/widgets/page_view/page_view.1.dart @@ -0,0 +1,95 @@ +// 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 a [PageView] using the `findChildIndexCallback` argument. +void main() => runApp(const PageViewExampleApp()); + +class PageViewExampleApp extends StatelessWidget { + const PageViewExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + return const MaterialApp(home: PageViewExample()); + } +} + +class PageViewExample extends StatefulWidget { + const PageViewExample({super.key}); + + @override + State createState() => _PageViewExampleState(); +} + +class _PageViewExampleState extends State { + List items = ['1', '2', '3', '4', '5']; + + void _reverse() { + setState(() { + items = items.reversed.toList(); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('PageView Sample')), + body: SafeArea( + child: PageView.custom( + childrenDelegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return KeepAliveItem( + data: items[index], + key: ValueKey(items[index]), + ); + }, + childCount: items.length, + findChildIndexCallback: (Key key) { + final ValueKey valueKey = key as ValueKey; + final String data = valueKey.value; + final int index = items.indexOf(data); + if (index >= 0) { + return index; + } + return null; + }, + ), + ), + ), + bottomNavigationBar: BottomAppBar( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () => _reverse(), + child: const Text('Reverse items'), + ), + ], + ), + ), + ); + } +} + +class KeepAliveItem extends StatefulWidget { + const KeepAliveItem({super.key, required this.data}); + + final String data; + + @override + State createState() => _KeepAliveItemState(); +} + +class _KeepAliveItemState extends State + with AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; + + @override + Widget build(BuildContext context) { + super.build(context); + return Text(widget.data); + } +} diff --git a/examples/api/lib/widgets/scroll_view/list_view.1.dart b/examples/api/lib/widgets/scroll_view/list_view.1.dart new file mode 100644 index 0000000000..46adcd3c67 --- /dev/null +++ b/examples/api/lib/widgets/scroll_view/list_view.1.dart @@ -0,0 +1,97 @@ +// 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 a [ListView.custom] using the `findChildIndexCallback` argument. +void main() => runApp(const ListViewExampleApp()); + +class ListViewExampleApp extends StatelessWidget { + const ListViewExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + return const MaterialApp(home: ListViewExample()); + } +} + +class ListViewExample extends StatefulWidget { + const ListViewExample({super.key}); + + @override + State createState() => _ListViewExampleState(); +} + +class _ListViewExampleState extends State { + List items = ['1', '2', '3', '4', '5']; + + void _reverse() { + setState(() { + items = items.reversed.toList(); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: ListView.custom( + childrenDelegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return KeepAliveItem( + data: items[index], + key: ValueKey(items[index]), + ); + }, + childCount: items.length, + findChildIndexCallback: (Key key) { + final ValueKey valueKey = key as ValueKey; + final String data = valueKey.value; + final int index = items.indexOf(data); + if (index >= 0) { + return index; + } + return null; + }, + ), + ), + ), + bottomNavigationBar: BottomAppBar( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () => _reverse(), + child: const Text('Reverse items'), + ), + ], + ), + ), + ); + } +} + +class KeepAliveItem extends StatefulWidget { + const KeepAliveItem({ + required Key key, + required this.data, + }) : super(key: key); + + final String data; + + @override + State createState() => _KeepAliveItemState(); +} + +class _KeepAliveItemState extends State + with AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; + + @override + Widget build(BuildContext context) { + super.build(context); + return Text(widget.data); + } +} diff --git a/examples/api/test/widgets/page_view/page_view.1_test.dart b/examples/api/test/widgets/page_view/page_view.1_test.dart new file mode 100644 index 0000000000..677a12e87e --- /dev/null +++ b/examples/api/test/widgets/page_view/page_view.1_test.dart @@ -0,0 +1,29 @@ +// 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/page_view/page_view.1.dart' + as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets( + 'tapping Reverse button should reverse PageView', + (WidgetTester tester) async { + await tester.pumpWidget(const example.PageViewExampleApp()); + final Finder pageView = find.byType(PageView); + final Finder reverseFinder = find.text('Reverse items'); + final Finder firstItemFinder = find.byKey(const ValueKey('1')); + final Finder lastItemFinder = find.byKey(const ValueKey('5')); + expect(pageView, findsOneWidget); + expect(reverseFinder, findsOneWidget); + expect(firstItemFinder, findsOneWidget); + expect(lastItemFinder, findsNothing); + await tester.tap(reverseFinder); + await tester.pump(); + expect(firstItemFinder, findsNothing); + expect(lastItemFinder, findsOneWidget); + }, + ); +} diff --git a/examples/api/test/widgets/scroll_view/list_view.1_test.dart b/examples/api/test/widgets/scroll_view/list_view.1_test.dart new file mode 100644 index 0000000000..0a9e5defbb --- /dev/null +++ b/examples/api/test/widgets/scroll_view/list_view.1_test.dart @@ -0,0 +1,28 @@ +// 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_view/list_view.1.dart' + as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets( + 'tapping Reverse button should reverse ListView', + (WidgetTester tester) async { + await tester.pumpWidget(const example.ListViewExampleApp()); + final Finder listView = find.byType(ListView); + final Finder reverseFinder = find.text('Reverse items'); + expect(listView, findsOneWidget); + expect(reverseFinder, findsOneWidget); + final Finder keepAliveItemFinder = find.byType(example.KeepAliveItem); + example.KeepAliveItem firstWidget = tester.firstWidget(keepAliveItemFinder); + expect(firstWidget.data, '1'); + await tester.tap(reverseFinder); + await tester.pump(); + firstWidget = tester.firstWidget(keepAliveItemFinder); + expect(firstWidget.data, '5'); + }, + ); +} diff --git a/packages/flutter/lib/src/widgets/page_view.dart b/packages/flutter/lib/src/widgets/page_view.dart index 6e973dbbe4..33a139e87e 100644 --- a/packages/flutter/lib/src/widgets/page_view.dart +++ b/packages/flutter/lib/src/widgets/page_view.dart @@ -711,84 +711,11 @@ class PageView extends StatefulWidget { /// Creates a scrollable list that works page by page with a custom child /// model. /// - /// {@tool snippet} - /// - /// This [PageView] uses a custom [SliverChildBuilderDelegate] to support child + /// {@tool dartpad} + /// This example shows a [PageView] that uses a custom [SliverChildBuilderDelegate] to support child /// reordering. /// - /// ```dart - /// class MyPageView extends StatefulWidget { - /// const MyPageView({super.key}); - /// - /// @override - /// State createState() => _MyPageViewState(); - /// } - /// - /// class _MyPageViewState extends State { - /// List items = ['1', '2', '3', '4', '5']; - /// - /// void _reverse() { - /// setState(() { - /// items = items.reversed.toList(); - /// }); - /// } - /// - /// @override - /// Widget build(BuildContext context) { - /// return Scaffold( - /// body: SafeArea( - /// child: PageView.custom( - /// childrenDelegate: SliverChildBuilderDelegate( - /// (BuildContext context, int index) { - /// return KeepAlive( - /// data: items[index], - /// key: ValueKey(items[index]), - /// ); - /// }, - /// childCount: items.length, - /// findChildIndexCallback: (Key key) { - /// final ValueKey valueKey = key as ValueKey; - /// final String data = valueKey.value; - /// return items.indexOf(data); - /// } - /// ), - /// ), - /// ), - /// bottomNavigationBar: BottomAppBar( - /// child: Row( - /// mainAxisAlignment: MainAxisAlignment.center, - /// children: [ - /// TextButton( - /// onPressed: () => _reverse(), - /// child: const Text('Reverse items'), - /// ), - /// ], - /// ), - /// ), - /// ); - /// } - /// } - /// - /// class KeepAlive extends StatefulWidget { - /// const KeepAlive({super.key, required this.data}); - /// - /// final String data; - /// - /// @override - /// State createState() => _KeepAliveState(); - /// } - /// - /// class _KeepAliveState extends State with AutomaticKeepAliveClientMixin{ - /// @override - /// bool get wantKeepAlive => true; - /// - /// @override - /// Widget build(BuildContext context) { - /// super.build(context); - /// return Text(widget.data); - /// } - /// } - /// ``` + /// ** See code in examples/api/lib/widgets/page_view/page_view.1.dart ** /// {@end-tool} /// /// {@macro flutter.widgets.PageView.allowImplicitScrolling} diff --git a/packages/flutter/lib/src/widgets/scroll_view.dart b/packages/flutter/lib/src/widgets/scroll_view.dart index 0e6b4ed248..067c69877f 100644 --- a/packages/flutter/lib/src/widgets/scroll_view.dart +++ b/packages/flutter/lib/src/widgets/scroll_view.dart @@ -1441,87 +1441,11 @@ class ListView extends BoxScrollView { /// For example, a custom child model can control the algorithm used to /// estimate the size of children that are not actually visible. /// - /// {@tool snippet} - /// - /// This [ListView] uses a custom [SliverChildBuilderDelegate] to support child + /// {@tool dartpad} + /// This example shows a [ListView] that uses a custom [SliverChildBuilderDelegate] to support child /// reordering. /// - /// ```dart - /// class MyListView extends StatefulWidget { - /// const MyListView({super.key}); - /// - /// @override - /// State createState() => _MyListViewState(); - /// } - /// - /// class _MyListViewState extends State { - /// List items = ['1', '2', '3', '4', '5']; - /// - /// void _reverse() { - /// setState(() { - /// items = items.reversed.toList(); - /// }); - /// } - /// - /// @override - /// Widget build(BuildContext context) { - /// return Scaffold( - /// body: SafeArea( - /// child: ListView.custom( - /// childrenDelegate: SliverChildBuilderDelegate( - /// (BuildContext context, int index) { - /// return KeepAlive( - /// data: items[index], - /// key: ValueKey(items[index]), - /// ); - /// }, - /// childCount: items.length, - /// findChildIndexCallback: (Key key) { - /// final ValueKey valueKey = key as ValueKey; - /// final String data = valueKey.value; - /// return items.indexOf(data); - /// } - /// ), - /// ), - /// ), - /// bottomNavigationBar: BottomAppBar( - /// child: Row( - /// mainAxisAlignment: MainAxisAlignment.center, - /// children: [ - /// TextButton( - /// onPressed: () => _reverse(), - /// child: const Text('Reverse items'), - /// ), - /// ], - /// ), - /// ), - /// ); - /// } - /// } - /// - /// class KeepAlive extends StatefulWidget { - /// const KeepAlive({ - /// required Key key, - /// required this.data, - /// }) : super(key: key); - /// - /// final String data; - /// - /// @override - /// State createState() => _KeepAliveState(); - /// } - /// - /// class _KeepAliveState extends State with AutomaticKeepAliveClientMixin{ - /// @override - /// bool get wantKeepAlive => true; - /// - /// @override - /// Widget build(BuildContext context) { - /// super.build(context); - /// return Text(widget.data); - /// } - /// } - /// ``` + /// ** See code in examples/api/lib/widgets/scroll_view/list_view.1.dart ** /// {@end-tool} const ListView.custom({ super.key,