mirror of
https://github.com/flutter/packages.git
synced 2025-06-28 22:02:38 +08:00
[rfw] Add some more documentation for RFW (#4349)
This commit is contained in:
@ -1,6 +1,11 @@
|
||||
## 1.0.11
|
||||
|
||||
* Adds more documentation in the README.md file!
|
||||
* Adds automated verification of the sample code in the README.
|
||||
|
||||
## 1.0.10
|
||||
|
||||
* Fixes stale ignore: prefer_const_constructors.
|
||||
* Fixes stale ignore: `prefer_const_constructors`.
|
||||
* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0.
|
||||
* Changes package internals to avoid explicit `as Uint8List` downcast.
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,10 @@ void main() {
|
||||
runApp(const Example());
|
||||
}
|
||||
|
||||
// The "#docregion" comment helps us keep this code in sync with the
|
||||
// excerpt in the rfw package's README.md file.
|
||||
//
|
||||
// #docregion Example
|
||||
class Example extends StatefulWidget {
|
||||
const Example({super.key});
|
||||
|
||||
@ -54,8 +58,11 @@ class _ExampleState extends State<Example> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Local widget library:
|
||||
_runtime.update(coreName, createCoreWidgets());
|
||||
// Remote widget library:
|
||||
_runtime.update(mainName, _remoteWidgets);
|
||||
// Configuration data:
|
||||
_data.update('greet', <String, Object>{'name': 'World'});
|
||||
}
|
||||
|
||||
@ -73,3 +80,4 @@ class _ExampleState extends State<Example> {
|
||||
);
|
||||
}
|
||||
}
|
||||
// #enddocregion Example
|
||||
|
@ -16,6 +16,10 @@ void main() {
|
||||
runApp(const Example());
|
||||
}
|
||||
|
||||
// The "#docregion" comment helps us keep this code in sync with the
|
||||
// excerpt in the rfw package's README.md file.
|
||||
//
|
||||
// #docregion Example
|
||||
class Example extends StatefulWidget {
|
||||
const Example({super.key});
|
||||
|
||||
@ -35,6 +39,9 @@ class _ExampleState extends State<Example> {
|
||||
|
||||
@override
|
||||
void reassemble() {
|
||||
// This function causes the Runtime to be updated any time the app is
|
||||
// hot reloaded, so that changes to _createLocalWidgets can be seen
|
||||
// during development. This function has no effect in production.
|
||||
super.reassemble();
|
||||
_update();
|
||||
}
|
||||
@ -87,3 +94,4 @@ class _ExampleState extends State<Example> {
|
||||
);
|
||||
}
|
||||
}
|
||||
// #enddocregion Example
|
||||
|
@ -25,7 +25,12 @@ widget CalculatorPad = Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// The "#docregion" pragma here allows us to inline this as an example
|
||||
// in the "rfw" package's README.md file. It is unrelated to what this
|
||||
// example is otherwise trying to show and can be disregarded.
|
||||
// #docregion button7
|
||||
CalculatorButton(label: "7", onPressed: event "digit" { arguments: [7] }),
|
||||
// #enddocregion button7
|
||||
CalculatorButton(label: "8", onPressed: event "digit" { arguments: [8] }),
|
||||
CalculatorButton(label: "9", onPressed: event "digit" { arguments: [9] }),
|
||||
SizedBox(width: 116.0, height: 116.0),
|
||||
@ -58,6 +63,7 @@ widget CalculatorPad = Column(
|
||||
],
|
||||
);
|
||||
|
||||
// #docregion CalculatorButton
|
||||
widget CalculatorButton = Padding(
|
||||
padding: [8.0],
|
||||
child: SizedBox(
|
||||
@ -69,7 +75,9 @@ widget CalculatorButton = Padding(
|
||||
),
|
||||
),
|
||||
);
|
||||
// #enddocregion CalculatorButton
|
||||
|
||||
// #docregion Button
|
||||
widget Button { down: false } = GestureDetector(
|
||||
onTap: args.onPressed,
|
||||
onTapDown: set state.down = true,
|
||||
@ -110,6 +118,7 @@ widget Button { down: false } = GestureDetector(
|
||||
),
|
||||
),
|
||||
);
|
||||
// #enddocregion Button
|
||||
|
||||
widget Display = Container(
|
||||
height: 80.0,
|
||||
|
@ -533,7 +533,7 @@ DynamicMap parseDataFile(String file) {
|
||||
/// event "..." { }
|
||||
/// ```
|
||||
///
|
||||
/// Tthe string is the name of the event, and the arguments map is the data to
|
||||
/// The string is the name of the event, and the arguments map is the data to
|
||||
/// send with the event.
|
||||
///
|
||||
/// For example, the event handler in the following sequence sends the event
|
||||
|
@ -537,6 +537,8 @@ Map<String, LocalWidgetBuilder> get _coreWidgetsDefinitions => <String, LocalWid
|
||||
);
|
||||
},
|
||||
|
||||
// The "#docregion" pragma below makes this accessible from the README.md file.
|
||||
// #docregion Row
|
||||
'Row': (BuildContext context, DataSource source) {
|
||||
return Row(
|
||||
mainAxisAlignment: ArgumentDecoders.enumValue<MainAxisAlignment>(MainAxisAlignment.values, source, ['mainAxisAlignment']) ?? MainAxisAlignment.start,
|
||||
@ -548,6 +550,7 @@ Map<String, LocalWidgetBuilder> get _coreWidgetsDefinitions => <String, LocalWid
|
||||
children: source.childList(['children']),
|
||||
);
|
||||
},
|
||||
// #enddocregion Row
|
||||
|
||||
'SafeArea': (BuildContext context, DataSource source) {
|
||||
return SafeArea(
|
||||
|
@ -2,7 +2,7 @@ name: rfw
|
||||
description: "Remote Flutter widgets: a library for rendering declarative widget description files at runtime."
|
||||
repository: https://github.com/flutter/packages/tree/main/packages/rfw
|
||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+rfw%22
|
||||
version: 1.0.10
|
||||
version: 1.0.11
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
308
packages/rfw/test/readme_test.dart
Normal file
308
packages/rfw/test/readme_test.dart
Normal file
@ -0,0 +1,308 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// This file is hand-formatted.
|
||||
|
||||
// This file contains and briefly tests the snippets used in the README.md file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:rfw/formats.dart';
|
||||
import 'package:rfw/rfw.dart';
|
||||
|
||||
const Map<String, String> rawRemoteWidgetSnippets = <String, String>{
|
||||
'root': '''
|
||||
// #docregion root
|
||||
import local;
|
||||
widget root = GreenBox(
|
||||
child: Hello(name: "World"),
|
||||
);
|
||||
// #enddocregion root
|
||||
''',
|
||||
|
||||
'fruit': '''
|
||||
import local;
|
||||
// #docregion fruit
|
||||
widget fruit = Foo(
|
||||
bar: { quux: [ 'apple', 'banana', 'cherry' ] },
|
||||
);
|
||||
// #enddocregion fruit
|
||||
''',
|
||||
|
||||
'example1': '''
|
||||
import local;
|
||||
// #docregion example1
|
||||
widget example1 = GreenBox(
|
||||
child: Foo(
|
||||
bar: 'Jean',
|
||||
),
|
||||
);
|
||||
// #enddocregion example1
|
||||
''',
|
||||
|
||||
'example2': '''
|
||||
import local;
|
||||
// #docregion example2
|
||||
widget example2 = GreenBox(
|
||||
child: Foo(
|
||||
bar: { name: 'Jean' },
|
||||
),
|
||||
);
|
||||
// #enddocregion example2
|
||||
''',
|
||||
|
||||
'example3': '''
|
||||
import local;
|
||||
// #docregion example3
|
||||
widget example3 = GreenBox(
|
||||
child: Foo(
|
||||
text: ['apple', 'banana']
|
||||
),
|
||||
);
|
||||
// #enddocregion example3
|
||||
''',
|
||||
|
||||
'tap': '''
|
||||
import local;
|
||||
import core;
|
||||
widget tap = GestureDetector(
|
||||
onTap: event 'test' { },
|
||||
child: SizedBox(),
|
||||
);
|
||||
''',
|
||||
|
||||
'tapDown': '''
|
||||
import local;
|
||||
import core;
|
||||
widget tapDown = GestureDetector(
|
||||
onTapDown: event 'test' { },
|
||||
child: SizedBox(),
|
||||
);
|
||||
''',
|
||||
|
||||
'Shop': '''
|
||||
// #docregion Shop
|
||||
import core;
|
||||
|
||||
widget Shop = ListView(
|
||||
children: [
|
||||
Text(text: "Products:"),
|
||||
...for product in data.server.games:
|
||||
Product(product: product)
|
||||
],
|
||||
);
|
||||
|
||||
widget Product = Text(text: args.product.name, softWrap: false, overflow: "fade");
|
||||
// #enddocregion Shop
|
||||
''',
|
||||
|
||||
'MaterialShop': '''
|
||||
// #docregion MaterialShop
|
||||
import core;
|
||||
import material;
|
||||
|
||||
widget MaterialShop = Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(text: ['Products']),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
...for product in data.server.games:
|
||||
Product(product: product)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
widget Product = ListTile(
|
||||
title: Text(text: args.product.name),
|
||||
onTap: event 'shop.productSelect' { name: args.product.name, path: args.product.link },
|
||||
);
|
||||
// #enddocregion MaterialShop
|
||||
''',
|
||||
};
|
||||
|
||||
// The empty docregion at the end of the following causes the snippet to end with "// ...".
|
||||
const String gameData =
|
||||
'''
|
||||
// #docregion game-data
|
||||
{ "games": [
|
||||
{"rating": 8.219, "users-rated": 16860, "name": "Twilight Struggle", "rank": 1, "link": "/boardgame/12333/twilight-struggle", "id": 12333},
|
||||
{"rating": 8.093, "users-rated": 11750, "name": "Through the Ages: A Story of Civilization", "rank": 2, "link": "/boardgame/25613/through-ages-story-civilization", "id": 25613},
|
||||
{"rating": 8.088, "users-rated": 34745, "name": "Agricola", "rank": 3, "link": "/boardgame/31260/agricola", "id": 31260},
|
||||
{"rating": 8.082, "users-rated": 8913, "name": "Terra Mystica", "rank": 4, "link": "/boardgame/120677/terra-mystica", "id": 120677},
|
||||
// #enddocregion game-data
|
||||
// #docregion game-data
|
||||
// #enddocregion game-data
|
||||
] }
|
||||
''';
|
||||
|
||||
List<WidgetLibrary> _createLocalWidgets(String region) {
|
||||
switch (region) {
|
||||
case 'root':
|
||||
return <WidgetLibrary>[LocalWidgetLibrary(<String, LocalWidgetBuilder>{
|
||||
// #docregion defaultLocalWidgets
|
||||
'GreenBox': (BuildContext context, DataSource source) {
|
||||
return Container(color: const Color(0xFF002211), child: source.child(<Object>['child']));
|
||||
},
|
||||
'Hello': (BuildContext context, DataSource source) {
|
||||
return Center(child: Text('Hello, ${source.v<String>(<Object>["name"])}!', textDirection: TextDirection.ltr));
|
||||
},
|
||||
// #enddocregion defaultLocalWidgets
|
||||
})];
|
||||
case 'fruit':
|
||||
return <WidgetLibrary>[
|
||||
LocalWidgetLibrary(<String, LocalWidgetBuilder>{
|
||||
// #docregion v
|
||||
'Foo': (BuildContext context, DataSource source) {
|
||||
return Text(source.v<String>(<Object>['bar', 'quux', 2])!);
|
||||
},
|
||||
// #enddocregion v
|
||||
}),
|
||||
LocalWidgetLibrary(<String, LocalWidgetBuilder>{
|
||||
// #docregion isList
|
||||
'Foo': (BuildContext context, DataSource source) {
|
||||
if (source.isList(<Object>['bar', 'quux'])) {
|
||||
return Text('${source.v<String>(<Object>['bar', 'quux', 2])}', textDirection: TextDirection.ltr);
|
||||
}
|
||||
return Text('${source.v<String>(<Object>['baz'])}', textDirection: TextDirection.ltr);
|
||||
},
|
||||
// #enddocregion isList
|
||||
}),
|
||||
];
|
||||
case 'example1':
|
||||
case 'example2':
|
||||
return <WidgetLibrary>[LocalWidgetLibrary(<String, LocalWidgetBuilder>{
|
||||
// #docregion child
|
||||
'GreenBox': (BuildContext context, DataSource source) {
|
||||
return Container(color: const Color(0xFF002211), child: source.child(<Object>['child']));
|
||||
},
|
||||
// #enddocregion child
|
||||
// #docregion isMap
|
||||
'Foo': (BuildContext context, DataSource source) {
|
||||
if (source.isMap(<Object>['bar'])) {
|
||||
return Text('${source.v<String>(<Object>['bar', 'name'])}', textDirection: TextDirection.ltr);
|
||||
}
|
||||
return Text('${source.v<String>(<Object>['bar'])}', textDirection: TextDirection.ltr);
|
||||
},
|
||||
// #enddocregion isMap
|
||||
})];
|
||||
case 'example3':
|
||||
return <WidgetLibrary>[LocalWidgetLibrary(<String, LocalWidgetBuilder>{
|
||||
// #docregion optionalChild
|
||||
'GreenBox': (BuildContext context, DataSource source) {
|
||||
return Container(color: const Color(0xFF002211), child: source.optionalChild(<Object>['child']));
|
||||
},
|
||||
// #enddocregion optionalChild
|
||||
// #docregion length
|
||||
'Foo': (BuildContext context, DataSource source) {
|
||||
final int length = source.length(<Object>['text']);
|
||||
if (length > 0) {
|
||||
final StringBuffer text = StringBuffer();
|
||||
for (int index = 0; index < length; index += 1) {
|
||||
text.write(source.v<String>(<Object>['text', index]));
|
||||
}
|
||||
return Text(text.toString(), textDirection: TextDirection.ltr);
|
||||
}
|
||||
return const Text('<empty>', textDirection: TextDirection.ltr);
|
||||
},
|
||||
// #enddocregion length
|
||||
})];
|
||||
case 'tap':
|
||||
// #docregion onTap
|
||||
return <WidgetLibrary>[
|
||||
LocalWidgetLibrary(<String, LocalWidgetBuilder>{
|
||||
// The local widget is called `GestureDetector`...
|
||||
'GestureDetector': (BuildContext context, DataSource source) {
|
||||
// The local widget is implemented using the `GestureDetector`
|
||||
// widget from the Flutter framework.
|
||||
return GestureDetector(
|
||||
onTap: source.voidHandler(<Object>['onTap']),
|
||||
// A full implementation of a `GestureDetector` local widget
|
||||
// would have more arguments here, like `onTapDown`, etc.
|
||||
child: source.optionalChild(<Object>['child']),
|
||||
);
|
||||
},
|
||||
}),
|
||||
];
|
||||
// #enddocregion onTap
|
||||
case 'tapDown':
|
||||
return <WidgetLibrary>[
|
||||
LocalWidgetLibrary(<String, LocalWidgetBuilder>{
|
||||
'GestureDetector': (BuildContext context, DataSource source) {
|
||||
// #docregion onTapDown
|
||||
return GestureDetector(
|
||||
onTapDown: source.handler(<Object>['onTapDown'], (HandlerTrigger trigger) => (TapDownDetails details) => trigger()),
|
||||
child: source.optionalChild(<Object>['child']),
|
||||
);
|
||||
// #enddocregion onTapDown
|
||||
},
|
||||
}),
|
||||
LocalWidgetLibrary(<String, LocalWidgetBuilder>{
|
||||
'GestureDetector': (BuildContext context, DataSource source) {
|
||||
// #docregion onTapDown-long
|
||||
return GestureDetector(
|
||||
// onTapDown expects a function that takes a TapDownDetails
|
||||
onTapDown: source.handler<GestureTapDownCallback>( // this returns a function that takes a TapDownDetails
|
||||
<Object>['onTapDown'],
|
||||
(HandlerTrigger trigger) { // "trigger" is the function that will send the event to RemoteWidget.onEvent
|
||||
return (TapDownDetails details) { // this is the function that is returned by handler() above
|
||||
trigger(); // the function calls "trigger"
|
||||
};
|
||||
},
|
||||
),
|
||||
child: source.optionalChild(<Object>['child']),
|
||||
);
|
||||
// #enddocregion onTapDown-long
|
||||
},
|
||||
}),
|
||||
LocalWidgetLibrary(<String, LocalWidgetBuilder>{
|
||||
'GestureDetector': (BuildContext context, DataSource source) {
|
||||
// #docregion onTapDown-position
|
||||
return GestureDetector(
|
||||
onTapDown: source.handler(<Object>['onTapDown'], (HandlerTrigger trigger) {
|
||||
return (TapDownDetails details) => trigger(<String, Object>{
|
||||
'x': details.globalPosition.dx,
|
||||
'y': details.globalPosition.dy,
|
||||
});
|
||||
}),
|
||||
child: source.optionalChild(<Object>['child']),
|
||||
);
|
||||
// #enddocregion onTapDown-position
|
||||
},
|
||||
}),
|
||||
];
|
||||
case 'Shop':
|
||||
case 'MaterialShop':
|
||||
return <WidgetLibrary>[];
|
||||
default:
|
||||
fail('test has no defined local widgets for root widget "$region"');
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('readme snippets', (WidgetTester tester) async {
|
||||
final Runtime runtime = Runtime()
|
||||
..update(const LibraryName(<String>['core']), createCoreWidgets())
|
||||
..update(const LibraryName(<String>['material']), createMaterialWidgets());
|
||||
final DynamicContent data = DynamicContent(parseDataFile(gameData));
|
||||
for (final String region in rawRemoteWidgetSnippets.keys) {
|
||||
final String body = rawRemoteWidgetSnippets[region]!;
|
||||
runtime.update(LibraryName(<String>[region]), parseLibraryFile(body));
|
||||
}
|
||||
for (final String region in rawRemoteWidgetSnippets.keys) {
|
||||
for (final WidgetLibrary localWidgets in _createLocalWidgets(region)) {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: RemoteWidget(
|
||||
runtime: runtime
|
||||
..update(const LibraryName(<String>['local']), localWidgets),
|
||||
data: data,
|
||||
widget: FullyQualifiedWidgetName(LibraryName(<String>[region]), region),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@ -26,6 +26,5 @@
|
||||
- plugin_platform_interface
|
||||
- pointer_interceptor
|
||||
- quick_actions/quick_actions
|
||||
- rfw
|
||||
- webview_flutter_android
|
||||
- webview_flutter_web
|
||||
|
Reference in New Issue
Block a user