Skip to content

Commit

Permalink
Add some cross references in the docs, move an example to a dartpad e…
Browse files Browse the repository at this point in the history
…xample (flutter#145571)

## Description

This adds some "See also" links to some docs for `TweenAnimationBuilder` and `ValueListenableBuilder`.

Also, moved a "snippet" example in `ValueListenableBuilder` into the examples directory as a Dartpad example.

## Tests
 - Added test for the example.
  • Loading branch information
gspencergoog committed Mar 22, 2024
1 parent 784f19c commit 0f685f8
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 55 deletions.
@@ -0,0 +1,94 @@
// 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 [ValueListenableBuilder].
void main() => runApp(const ValueListenableBuilderExampleApp());

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

@override
Widget build(BuildContext context) {
return const MaterialApp(
home: ValueListenableBuilderExample(),
);
}
}

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

@override
State<ValueListenableBuilderExample> createState() => _ValueListenableBuilderExampleState();
}

class _ValueListenableBuilderExampleState extends State<ValueListenableBuilderExample> {
final ValueNotifier<int> _counter = ValueNotifier<int>(0);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ValueListenableBuilder Sample'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
ValueListenableBuilder<int>(
builder: (BuildContext context, int value, Widget? child) {
// This builder will only get called when the _counter
// is updated.
return Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
CountDisplay(count: value),
child!,
],
);
},
valueListenable: _counter,
// The child parameter is most helpful if the child is
// expensive to build and does not depend on the value from
// the notifier.
child: const Padding(
padding: EdgeInsets.all(10.0),
child: SizedBox(
width: 40,
height: 40,
child: FlutterLogo(size: 40),
),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.plus_one),
onPressed: () => _counter.value += 1,
),
);
}
}

class CountDisplay extends StatelessWidget {
const CountDisplay({super.key, required this.count});

final int count;

@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
padding: const EdgeInsetsDirectional.all(10),
child: Text('$count', style: Theme.of(context).textTheme.headline4),
);
}
}
@@ -0,0 +1,35 @@
// 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/value_listenable_builder/value_listenable_builder.0.dart' as example;
import 'package:flutter_test/flutter_test.dart';

void main() {
testWidgets('Tapping FAB increments counter', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: example.ValueListenableBuilderExample(),
),
);

String getCount() {
return (tester.widget(
find.descendant(
of: find.byType(example.CountDisplay),
matching: find.byType(Text),
),
) as Text).data!;
}

expect(find.text('You have pushed the button this many times:'), findsOneWidget);
expect(find.text('0'), findsOneWidget);
expect(find.byIcon(Icons.plus_one), findsOneWidget);
expect(getCount(), equals('0'));

await tester.tap(find.byType(FloatingActionButton).first);
await tester.pumpAndSettle();
expect(getCount(), equals('1'));
});
}
5 changes: 5 additions & 0 deletions packages/flutter/lib/src/widgets/tween_animation_builder.dart
Expand Up @@ -90,6 +90,11 @@ import 'value_listenable_builder.dart';
/// [AnimatedBuilder], which can be used similarly to this
/// [TweenAnimationBuilder], but unlike the latter it is powered by a
/// developer-managed [AnimationController].
///
/// See also:
///
/// * [ValueListenableBuilder], a widget whose content stays synced with a
/// [ValueListenable] instead of a [Tween].
class TweenAnimationBuilder<T extends Object?> extends ImplicitlyAnimatedWidget {
/// Creates a [TweenAnimationBuilder].
///
Expand Down
60 changes: 5 additions & 55 deletions packages/flutter/lib/src/widgets/value_listenable_builder.dart
Expand Up @@ -40,63 +40,11 @@ typedef ValueWidgetBuilder<T> = Widget Function(BuildContext context, T value, W
/// Using this pre-built child is entirely optional, but can improve
/// performance significantly in some cases and is therefore a good practice.
///
/// {@tool snippet}
///
/// {@tool dartpad}
/// This sample shows how you could use a [ValueListenableBuilder] instead of
/// setting state on the whole [Scaffold] in the default `flutter create` app.
///
/// ```dart
/// class MyHomePage extends StatefulWidget {
/// const MyHomePage({super.key, required this.title});
/// final String title;
///
/// @override
/// State<MyHomePage> createState() => _MyHomePageState();
/// }
/// setting state on the whole [Scaffold] in a counter app.
///
/// class _MyHomePageState extends State<MyHomePage> {
/// final ValueNotifier<int> _counter = ValueNotifier<int>(0);
/// final Widget goodJob = const Text('Good job!');
/// @override
/// Widget build(BuildContext context) {
/// return Scaffold(
/// appBar: AppBar(
/// title: Text(widget.title)
/// ),
/// body: Center(
/// child: Column(
/// mainAxisAlignment: MainAxisAlignment.center,
/// children: <Widget>[
/// const Text('You have pushed the button this many times:'),
/// ValueListenableBuilder<int>(
/// builder: (BuildContext context, int value, Widget? child) {
/// // This builder will only get called when the _counter
/// // is updated.
/// return Row(
/// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
/// children: <Widget>[
/// Text('$value'),
/// child!,
/// ],
/// );
/// },
/// valueListenable: _counter,
/// // The child parameter is most helpful if the child is
/// // expensive to build and does not depend on the value from
/// // the notifier.
/// child: goodJob,
/// )
/// ],
/// ),
/// ),
/// floatingActionButton: FloatingActionButton(
/// child: const Icon(Icons.plus_one),
/// onPressed: () => _counter.value += 1,
/// ),
/// );
/// }
/// }
/// ```
/// ** See code in examples/api/lib/widgets/value_listenable_builder/value_listenable_builder.0.dart **
/// {@end-tool}
///
/// See also:
Expand All @@ -108,6 +56,8 @@ typedef ValueWidgetBuilder<T> = Widget Function(BuildContext context, T value, W
/// you have a direct reference to.
/// * [StreamBuilder], where a builder can depend on a [Stream] rather than
/// a [ValueListenable] for more advanced use cases.
/// * [TweenAnimationBuilder], which can animate values in a widget based on a
/// [Tween].
class ValueListenableBuilder<T> extends StatefulWidget {
/// Creates a [ValueListenableBuilder].
///
Expand Down

0 comments on commit 0f685f8

Please sign in to comment.