InheritedModel
: Add a complete interactive example and update snippet for null safety (#104174)
This commit is contained in:
parent
c2cc31b198
commit
71a9ccbf2c
165
examples/api/lib/widgets/inherited_model/inherited_model.0.dart
Normal file
165
examples/api/lib/widgets/inherited_model/inherited_model.0.dart
Normal file
@ -0,0 +1,165 @@
|
||||
// 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),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
// 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user