Added an example for IndexedStack widget (#105318)
* Added an example for IndexedStack * Added tests for the IndexedStack example * Fixed type issue for onSubmitted callback functions * Fixed documentation and moved files to their appropriate places * Fixed documentation and moved files to their appropriate places * Moved test files to their appropriate places * Moved test files to their appropriate places * Fixed file path in documentation * Remove trailing space * Formatting changes * Remove extra line * Further formatting changes * Further formatting changes * fix comma and inline Co-authored-by: Greg Spencer <gspencergoog@users.noreply.github.com> * Formatting * indentation and formatting * Formatting * Formatting * Formatting * Removed duplicate chevron * better wording on documentation Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com> * Added testing for state preservation Co-authored-by: Greg Spencer <gspencergoog@users.noreply.github.com> Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
This commit is contained in:
parent
38df107b8c
commit
082560313b
144
examples/api/lib/widgets/basic/indexed_stack.0.dart
Normal file
144
examples/api/lib/widgets/basic/indexed_stack.0.dart
Normal file
@ -0,0 +1,144 @@
|
||||
// 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.
|
||||
|
||||
// Flutter code sample for IndexedStack.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() => runApp(const MyApp());
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
static const String _title = 'Flutter Code Sample';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: _title,
|
||||
home: Scaffold(
|
||||
appBar: AppBar(title: const Text(_title)),
|
||||
body: const MyStatefulWidget(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyStatefulWidget extends StatefulWidget {
|
||||
const MyStatefulWidget({super.key});
|
||||
|
||||
@override
|
||||
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
|
||||
}
|
||||
|
||||
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
|
||||
List<String> names = <String>['Dash', 'John', 'Mary'];
|
||||
int index = 0;
|
||||
final TextEditingController fieldText = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
width: 300,
|
||||
child: TextField(
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: 'Enter the name for a person to track',
|
||||
),
|
||||
onSubmitted: (String value) {
|
||||
setState(() {
|
||||
names.add(value);
|
||||
});
|
||||
fieldText.clear();
|
||||
},
|
||||
controller: fieldText,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 50),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (index == 0) {
|
||||
index = names.length - 1;
|
||||
} else {
|
||||
index -= 1;
|
||||
}
|
||||
});
|
||||
},
|
||||
child: const Icon(key: Key('gesture1'), Icons.chevron_left),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
IndexedStack(
|
||||
index: index,
|
||||
children: <Widget>[
|
||||
for (String name in names) PersonTracker(name: name)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (index == names.length - 1) {
|
||||
index = 0;
|
||||
} else {
|
||||
index += 1;
|
||||
}
|
||||
});
|
||||
},
|
||||
child: const Icon(key: Key('gesture2'), Icons.chevron_right),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PersonTracker extends StatefulWidget {
|
||||
const PersonTracker({super.key, required this.name});
|
||||
final String name;
|
||||
@override
|
||||
State<PersonTracker> createState() => _PersonTrackerState();
|
||||
}
|
||||
|
||||
class _PersonTrackerState extends State<PersonTracker> {
|
||||
int counter = 0;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
key: Key(widget.name),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color.fromARGB(255, 239, 248, 255),
|
||||
border: Border.all(color: const Color.fromARGB(255, 54, 60, 244)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Text('Name: ${widget.name}'),
|
||||
Text('Score: $counter'),
|
||||
TextButton.icon(
|
||||
key: Key('increment${widget.name}'),
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
counter += 1;
|
||||
});
|
||||
},
|
||||
label: const Text('Increment'),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
93
examples/api/test/widgets/basic/indexed_stack.0_test.dart
Normal file
93
examples/api/test/widgets/basic/indexed_stack.0_test.dart
Normal file
@ -0,0 +1,93 @@
|
||||
// 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/basic/indexed_stack.0.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('has correct forward rendering mechanism', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const example.MyApp());
|
||||
|
||||
final Finder gesture2 = find.byKey(const Key('gesture2'));
|
||||
final Element containerFinder = find.byKey(const Key('Dash')).evaluate().first;
|
||||
expect(containerFinder.renderObject!.debugNeedsPaint, false);
|
||||
final Element containerFinder1 = find.byKey(const Key('John')).evaluate().first;
|
||||
expect(containerFinder1.renderObject!.debugNeedsPaint, true);
|
||||
final Element containerFinder2 = find.byKey(const Key('Mary')).evaluate().first;
|
||||
expect(containerFinder2.renderObject!.debugNeedsPaint, true);
|
||||
|
||||
await tester.tap(gesture2);
|
||||
await tester.pump();
|
||||
expect(containerFinder.renderObject!.debugNeedsPaint, false);
|
||||
expect(containerFinder1.renderObject!.debugNeedsPaint, false);
|
||||
expect(containerFinder2.renderObject!.debugNeedsPaint, true);
|
||||
|
||||
await tester.tap(gesture2);
|
||||
await tester.pump();
|
||||
expect(containerFinder.renderObject!.debugNeedsPaint, false);
|
||||
expect(containerFinder1.renderObject!.debugNeedsPaint, false);
|
||||
expect(containerFinder2.renderObject!.debugNeedsPaint, false);
|
||||
});
|
||||
testWidgets('has correct backward rendering mechanism', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const example.MyApp());
|
||||
|
||||
final Finder gesture1 = find.byKey(const Key('gesture1'));
|
||||
final Element containerFinder = find.byKey(const Key('Dash')).evaluate().first;
|
||||
final Element containerFinder1 = find.byKey(const Key('John')).evaluate().first;
|
||||
final Element containerFinder2 = find.byKey(const Key('Mary')).evaluate().first;
|
||||
|
||||
await tester.tap(gesture1);
|
||||
await tester.pump();
|
||||
expect(containerFinder.renderObject!.debugNeedsPaint, false);
|
||||
expect(containerFinder1.renderObject!.debugNeedsPaint, true);
|
||||
expect(containerFinder2.renderObject!.debugNeedsPaint, false);
|
||||
|
||||
await tester.tap(gesture1);
|
||||
await tester.pump();
|
||||
expect(containerFinder.renderObject!.debugNeedsPaint, false);
|
||||
expect(containerFinder1.renderObject!.debugNeedsPaint, false);
|
||||
expect(containerFinder2.renderObject!.debugNeedsPaint, false);
|
||||
});
|
||||
testWidgets('has correct element addition handling', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const example.MyApp());
|
||||
|
||||
expect(find.byType(example.PersonTracker), findsNWidgets(3));
|
||||
final Finder textField = find.byType(TextField);
|
||||
await tester.enterText(textField, 'hello');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pump();
|
||||
expect(find.byType(example.PersonTracker), findsNWidgets(4));
|
||||
|
||||
await tester.enterText(textField, 'hello1');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pump();
|
||||
expect(find.byType(example.PersonTracker), findsNWidgets(5));
|
||||
});
|
||||
testWidgets('has state preservation', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const example.MyApp());
|
||||
|
||||
final Finder gesture1 = find.byKey(const Key('gesture1'));
|
||||
final Finder gesture2 = find.byKey(const Key('gesture2'));
|
||||
final Finder containerFinder = find.byKey(const Key('Dash'));
|
||||
final Finder incrementFinder = find.byKey(const Key('incrementDash'));
|
||||
Finder counterFinder(int score) {
|
||||
return find.descendant(of: containerFinder, matching: find.text('Score: $score'));
|
||||
}
|
||||
|
||||
expect(counterFinder(0), findsOneWidget);
|
||||
await tester.tap(incrementFinder);
|
||||
await tester.pump();
|
||||
|
||||
expect(counterFinder(1), findsOneWidget);
|
||||
|
||||
await tester.tap(gesture2);
|
||||
await tester.pump();
|
||||
await tester.tap(gesture1);
|
||||
await tester.pump();
|
||||
|
||||
expect(counterFinder(1), findsOneWidget);
|
||||
expect(counterFinder(0), findsNothing);
|
||||
});
|
||||
}
|
@ -3846,6 +3846,13 @@ class Stack extends MultiChildRenderObjectWidget {
|
||||
///
|
||||
/// {@youtube 560 315 https://www.youtube.com/watch?v=_O0PPD1Xfbk}
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This example shows a [IndexedStack] widget being used to lay out one card
|
||||
/// at a time from a series of cards, each keeping their respective states.
|
||||
///
|
||||
/// ** See code in examples/api/lib/widgets/basic/indexed_stack.0.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Stack], for more details about stacks.
|
||||
|
Loading…
x
Reference in New Issue
Block a user