Unverified Commit 08256031 authored by Youssef Attia's avatar Youssef Attia Committed by GitHub

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: 's avatarGreg Spencer <gspencergoog@users.noreply.github.com>

* Formatting

* indentation and formatting

* Formatting

* Formatting

* Formatting

* Removed duplicate chevron

* better wording on documentation
Co-authored-by: 's avatarTong Mu <dkwingsmt@users.noreply.github.com>

* Added testing for state preservation
Co-authored-by: 's avatarGreg Spencer <gspencergoog@users.noreply.github.com>
Co-authored-by: 's avatarTong Mu <dkwingsmt@users.noreply.github.com>
parent 38df107b
// 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'),
)
],
),
);
}
}
// 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 { ...@@ -3846,6 +3846,13 @@ class Stack extends MultiChildRenderObjectWidget {
/// ///
/// {@youtube 560 315 https://www.youtube.com/watch?v=_O0PPD1Xfbk} /// {@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: /// See also:
/// ///
/// * [Stack], for more details about stacks. /// * [Stack], for more details about stacks.
......
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