Unverified Commit 71a9ccbf authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

`InheritedModel`: Add a complete interactive example and update snippet for null safety (#104174)

parent c2cc31b1
// 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 InheritedModel
import 'package:flutter/material.dart';
enum LogoAspect { backgroundColor, large }
void main() => runApp(const InheritedModelApp());
class InheritedModelApp extends StatelessWidget {
const InheritedModelApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: InheritedModelExample(),
);
}
}
class LogoModel extends InheritedModel<LogoAspect> {
const LogoModel({
super.key,
this.backgroundColor,
this.large,
required super.child,
});
final Color? backgroundColor;
final bool? large;
static Color? backgroundColorOf(BuildContext context) {
return InheritedModel.inheritFrom<LogoModel>(context, aspect: LogoAspect.backgroundColor)?.backgroundColor;
}
static bool sizeOf(BuildContext context) {
return InheritedModel.inheritFrom<LogoModel>(context, aspect: LogoAspect.large)?.large ?? false;
}
@override
bool updateShouldNotify(LogoModel oldWidget) {
return backgroundColor != oldWidget.backgroundColor ||
large != oldWidget.large;
}
@override
bool updateShouldNotifyDependent(LogoModel oldWidget, Set<LogoAspect> dependencies) {
if (backgroundColor != oldWidget.backgroundColor && dependencies.contains(LogoAspect.backgroundColor)) {
return true;
}
if (large != oldWidget.large && dependencies.contains(LogoAspect.large)) {
return true;
}
return false;
}
}
class InheritedModelExample extends StatefulWidget {
const InheritedModelExample({super.key});
@override
State<InheritedModelExample> createState() => _InheritedModelExampleState();
}
class _InheritedModelExampleState extends State<InheritedModelExample> {
bool large = false;
Color color = Colors.blue;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('InheritedModel Sample')),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Center(
child: LogoModel(
backgroundColor: color,
large: large,
child: const BackgroundWidget(
child: LogoWidget(),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Rebuilt Background'),
duration: Duration(milliseconds: 500),
),
);
setState(() {
if (color == Colors.blue){
color = Colors.red;
} else {
color = Colors.blue;
}
});
},
child: const Text('Update background'),
),
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Rebuilt LogoWidget'),
duration: Duration(milliseconds: 500),
),
);
setState(() {
large = !large;
});
},
child: const Text('Resize Logo'),
),
],
)
],
),
);
}
}
class BackgroundWidget extends StatelessWidget {
const BackgroundWidget({super.key, required this.child});
final Widget child;
@override
Widget build(BuildContext context) {
final Color color = LogoModel.backgroundColorOf(context)!;
return AnimatedContainer(
padding: const EdgeInsets.all(12.0),
color: color,
duration: const Duration(seconds: 2),
curve: Curves.fastOutSlowIn,
child: child,
);
}
}
class LogoWidget extends StatelessWidget {
const LogoWidget({super.key});
@override
Widget build(BuildContext context) {
final bool largeLogo = LogoModel.sizeOf(context);
return AnimatedContainer(
padding: const EdgeInsets.all(20.0),
duration: const Duration(seconds: 2),
curve: Curves.fastLinearToSlowEaseIn,
alignment: Alignment.center,
child: FlutterLogo(size: largeLogo ? 200.0 : 100.0),
);
}
}
// 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/inherited_model/inherited_model.0.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Rebuild widget using InheritedModel', (WidgetTester tester) async {
await tester.pumpWidget(
const example.InheritedModelApp(),
);
BoxDecoration? decoration = tester.widget<AnimatedContainer>(
find.byType(AnimatedContainer).first,
).decoration as BoxDecoration?;
expect(decoration!.color, Colors.blue);
await tester.tap(find.text('Update background'));
await tester.pumpAndSettle();
decoration = tester.widget<AnimatedContainer>(
find.byType(AnimatedContainer).first,
).decoration as BoxDecoration?;
expect(decoration!.color, Colors.red);
double? size = tester.widget<FlutterLogo>(find.byType(FlutterLogo)).size;
expect(size, 100.0);
await tester.tap(find.text('Resize Logo'));
await tester.pumpAndSettle();
size = tester.widget<FlutterLogo>(find.byType(FlutterLogo)).size;
expect(size, 200.0);
});
}
......@@ -34,7 +34,7 @@ import 'framework.dart';
/// ```dart
/// class MyModel extends InheritedModel<String> {
/// // ...
/// static MyModel of(BuildContext context, String aspect) {
/// static MyModel? of(BuildContext context, String aspect) {
/// return InheritedModel.inheritFrom<MyModel>(context, aspect: aspect);
/// }
/// }
......@@ -44,37 +44,42 @@ import 'framework.dart';
/// be rebuilt when the `foo` aspect of `MyModel` changes. If the aspect
/// is null, then the model supports all aspects.
///
/// {@tool snippet}
/// When the inherited model is rebuilt the [updateShouldNotify] and
/// [updateShouldNotifyDependent] methods are used to decide what
/// should be rebuilt. If [updateShouldNotify] returns true, then the
/// inherited model's [updateShouldNotifyDependent] method is tested for
/// each dependent and the set of aspect objects it depends on.
/// The [updateShouldNotifyDependent] method must compare the set of aspect
/// dependencies with the changes in the model itself.
///
/// For example:
/// dependencies with the changes in the model itself. For example:
///
/// ```dart
/// class ABModel extends InheritedModel<String> {
/// ABModel({ this.a, this.b, Widget child }) : super(child: child);
/// const ABModel({
/// super.key,
/// this.a,
/// this.b,
/// required super.child,
/// });
///
/// final int a;
/// final int b;
/// final int? a;
/// final int? b;
///
/// @override
/// bool updateShouldNotify(ABModel old) {
/// return a != old.a || b != old.b;
/// bool updateShouldNotify(ABModel oldWidget) {
/// return a != oldWidget.a || b != oldWidget.b;
/// }
///
/// @override
/// bool updateShouldNotifyDependent(ABModel old, Set<String> aspects) {
/// return (a != old.a && aspects.contains('a'))
/// || (b != old.b && aspects.contains('b'))
/// bool updateShouldNotifyDependent(ABModel oldWidget, Set<String> dependencies) {
/// return (a != oldWidget.a && dependencies.contains('a'))
/// || (b != oldWidget.b && dependencies.contains('b'));
/// }
///
/// // ...
/// }
/// ```
/// {@end-tool}
///
/// In the previous example the dependencies checked by
/// [updateShouldNotifyDependent] are just the aspect strings passed to
......@@ -84,6 +89,14 @@ import 'framework.dart';
/// then changes in the model will cause the widget to be rebuilt
/// unconditionally.
///
/// {@tool dartpad}
/// This example shows how to implement [InheritedModel] to rebuild a
/// widget based on a qualified dependence. When tapped on the "Resize Logo" button
/// only the logo widget is rebuilt while the background widget remains unaffected.
///
/// ** See code in examples/api/lib/widgets/inherited_model/inherited_model.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [InheritedWidget], an inherited widget that only notifies dependents
......
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