mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Script to generate widget theme data defaults from the Material token database. (#95370)
This commit is contained in:
parent
a9c43bf752
commit
78f53bed51
@ -874,6 +874,7 @@ Future<void> _runFrameworkTests() async {
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'android_semantics_testing'), fatalWarnings: false);
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'manual_tests'));
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'vitool'));
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'gen_defaults'));
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'gen_keycodes'));
|
||||
await _runFlutterTest(path.join(flutterRoot, 'dev', 'benchmarks', 'test_apps', 'stocks'));
|
||||
await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'), tests: <String>[path.join('test', 'src', 'real_tests')], options: soundNullSafetyOptions);
|
||||
|
27
dev/tools/gen_defaults/README.md
Normal file
27
dev/tools/gen_defaults/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
## Token Defaults Generator
|
||||
|
||||
Script that generates widget component theme data defaults
|
||||
based on the Material Token database. These tokens were
|
||||
extracted into a JSON file from an internal Google database.
|
||||
|
||||
## Usage
|
||||
Run this program from the root of the git repository:
|
||||
```
|
||||
dart dev/tools/gen_defaults/bin/gen_defaults.dart
|
||||
```
|
||||
|
||||
## Templates
|
||||
|
||||
There is a template file for every component that needs defaults from
|
||||
the token database. These templates are implemented as subclasses of
|
||||
`TokenTemplate`. This base class provides some utilities and a structure
|
||||
for adding a new chunk of generated code to the bottom of a given file.
|
||||
|
||||
Templates need to override the `generate` method to provide the generated
|
||||
code chunk as a string. The tokens are represented as a `Map<String, dynamic>`
|
||||
that is loaded from `data/material-tokens.json`. Templates can look up
|
||||
whatever properties are needed in this structure to provide the properties
|
||||
needed for the component.
|
||||
|
||||
See `lib/fab_template.dart` for an example that generates defaults for the
|
||||
Floating Action Button.
|
29
dev/tools/gen_defaults/bin/gen_defaults.dart
Normal file
29
dev/tools/gen_defaults/bin/gen_defaults.dart
Normal file
@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
// Generate component theme data defaults based on the Material
|
||||
// Design Token database. These tokens were extracted into a
|
||||
// JSON file from the internal Google database.
|
||||
//
|
||||
// ## Usage
|
||||
//
|
||||
// Run this program from the root of the git repository.
|
||||
//
|
||||
// ```
|
||||
// dart dev/tools/gen_defaults/bin/gen_defaults.dart
|
||||
// ```
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:gen_defaults/fab_template.dart';
|
||||
|
||||
Future<void> main(List<String> args) async {
|
||||
const String tokensDB = 'dev/tools/gen_defaults/data/material-tokens.json';
|
||||
final Map<String, dynamic> tokens = jsonDecode(File(tokensDB).readAsStringSync()) as Map<String, dynamic>;
|
||||
|
||||
const String materialLib = 'packages/flutter/lib/src/material';
|
||||
|
||||
FABTemplate('$materialLib/floating_action_button.dart', tokens).updateFile();
|
||||
}
|
1044
dev/tools/gen_defaults/data/material-tokens.json
Normal file
1044
dev/tools/gen_defaults/data/material-tokens.json
Normal file
File diff suppressed because it is too large
Load Diff
90
dev/tools/gen_defaults/lib/fab_template.dart
Normal file
90
dev/tools/gen_defaults/lib/fab_template.dart
Normal file
@ -0,0 +1,90 @@
|
||||
// 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 'template.dart';
|
||||
|
||||
class FABTemplate extends TokenTemplate {
|
||||
const FABTemplate(String fileName, Map<String, dynamic> tokens) : super(fileName, tokens);
|
||||
|
||||
@override
|
||||
String generate() => '''
|
||||
// Generated version ${tokens["version"]}, ${tokens["date"]}
|
||||
class _M3Defaults extends FloatingActionButtonThemeData {
|
||||
_M3Defaults(this.context, this.type, this.hasChild)
|
||||
: _colors = Theme.of(context).colorScheme,
|
||||
_textTheme = Theme.of(context).textTheme;
|
||||
|
||||
final BuildContext context;
|
||||
final _FloatingActionButtonType type;
|
||||
final bool hasChild;
|
||||
final ColorScheme _colors;
|
||||
final TextTheme _textTheme;
|
||||
|
||||
bool get _isExtended => type == _FloatingActionButtonType.extended;
|
||||
|
||||
@override Color? get foregroundColor => _colors.${color("md.comp.fab.primary.icon")};
|
||||
@override Color? get backgroundColor => _colors.${color("md.comp.fab.primary.container")};
|
||||
@override Color? get splashColor => _colors.${color("md.comp.fab.primary.pressed.state-layer")};
|
||||
@override double get elevation => ${elevation("md.comp.fab.primary.container")};
|
||||
@override Color? get focusColor => _colors.${color("md.comp.fab.primary.focus.state-layer")};
|
||||
@override double get focusElevation => ${elevation("md.comp.fab.primary.focus.container")};
|
||||
@override Color? get hoverColor => _colors.${color("md.comp.fab.primary.hover.state-layer")};
|
||||
@override double get hoverElevation => ${elevation("md.comp.fab.primary.hover.container")};
|
||||
@override double get highlightElevation => ${elevation("md.comp.fab.primary.pressed.container")};
|
||||
|
||||
@override
|
||||
ShapeBorder? get shape {
|
||||
switch (type) {
|
||||
case _FloatingActionButtonType.regular:
|
||||
return ${shape("md.comp.fab.primary.container.shape")};
|
||||
case _FloatingActionButtonType.small:
|
||||
return ${shape("md.comp.fab.primary.small.container.shape")};
|
||||
case _FloatingActionButtonType.large:
|
||||
return ${shape("md.comp.fab.primary.large.container.shape")};
|
||||
case _FloatingActionButtonType.extended:
|
||||
return ${shape("md.comp.extended-fab.primary.container.shape")};
|
||||
}
|
||||
}
|
||||
|
||||
@override bool? get enableFeedback => true;
|
||||
|
||||
@override
|
||||
double? get iconSize {
|
||||
switch (type) {
|
||||
case _FloatingActionButtonType.regular: return ${value("md.comp.fab.primary.icon.size")};
|
||||
case _FloatingActionButtonType.small: return ${value("md.comp.fab.primary.small.icon.size")};
|
||||
case _FloatingActionButtonType.large: return ${value("md.comp.fab.primary.large.icon.size")};
|
||||
case _FloatingActionButtonType.extended: return ${value("md.comp.extended-fab.primary.icon.size")};
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
BoxConstraints? get sizeConstraints => const BoxConstraints.tightFor(
|
||||
width: ${value("md.comp.fab.primary.container.width")},
|
||||
height: ${value("md.comp.fab.primary.container.height")},
|
||||
);
|
||||
|
||||
@override
|
||||
BoxConstraints? get smallSizeConstraints => const BoxConstraints.tightFor(
|
||||
width: ${value("md.comp.fab.primary.small.container.width")},
|
||||
height: ${value("md.comp.fab.primary.small.container.height")},
|
||||
);
|
||||
|
||||
@override
|
||||
BoxConstraints? get largeSizeConstraints => const BoxConstraints.tightFor(
|
||||
width: ${value("md.comp.fab.primary.large.container.width")},
|
||||
height: ${value("md.comp.fab.primary.large.container.height")},
|
||||
);
|
||||
|
||||
@override
|
||||
BoxConstraints? get extendedSizeConstraints => const BoxConstraints.tightFor(
|
||||
height: ${value("md.comp.extended-fab.primary.container.height")},
|
||||
);
|
||||
|
||||
@override double? get extendedIconLabelSpacing => 8.0;
|
||||
@override EdgeInsetsGeometry? get extendedPadding => EdgeInsetsDirectional.only(start: hasChild && _isExtended ? 16.0 : 20.0, end: 20.0);
|
||||
@override TextStyle? get extendedTextStyle => _textTheme.${textStyle("md.comp.extended-fab.primary.label-text")};
|
||||
}
|
||||
''';
|
||||
}
|
91
dev/tools/gen_defaults/lib/template.dart
Normal file
91
dev/tools/gen_defaults/lib/template.dart
Normal file
@ -0,0 +1,91 @@
|
||||
// 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 'dart:io';
|
||||
|
||||
abstract class TokenTemplate {
|
||||
const TokenTemplate(this.fileName, this.tokens);
|
||||
|
||||
static const String beginGeneratedComment = '''
|
||||
|
||||
// BEGIN GENERATED TOKEN PROPERTIES
|
||||
''';
|
||||
|
||||
static const String headerComment = '''
|
||||
|
||||
// Generated code to the end of this file. Do not edit by hand.
|
||||
// These defaults are generated from the Material Design Token
|
||||
// database by the script dev/tools/gen_defaults/bin/gen_defaults.dart.
|
||||
|
||||
''';
|
||||
|
||||
static const String endGeneratedComment = '''
|
||||
|
||||
// END GENERATED TOKEN PROPERTIES
|
||||
''';
|
||||
|
||||
final String fileName;
|
||||
final Map<String, dynamic> tokens;
|
||||
|
||||
/// Replace or append the contents of the file with the text from [generate].
|
||||
///
|
||||
/// If the file already contains generated block at the end, it will
|
||||
/// be replaced by the [generate] output. Otherwise the content will
|
||||
/// just be appended to the end of the file.
|
||||
Future<void> updateFile() async {
|
||||
String contents = File(fileName).readAsStringSync();
|
||||
final int previousGeneratedIndex = contents.indexOf(beginGeneratedComment);
|
||||
if (previousGeneratedIndex != -1) {
|
||||
contents = contents.substring(0, previousGeneratedIndex);
|
||||
}
|
||||
final StringBuffer buffer = StringBuffer(contents);
|
||||
buffer.write(beginGeneratedComment);
|
||||
buffer.write(headerComment);
|
||||
buffer.write(generate());
|
||||
buffer.write(endGeneratedComment);
|
||||
File(fileName).writeAsStringSync(buffer.toString());
|
||||
}
|
||||
|
||||
/// Provide the generated content for the template.
|
||||
///
|
||||
/// This abstract method needs to be implemented by subclasses
|
||||
/// to provide the content that [updateFile] will append to the
|
||||
/// bottom of the file.
|
||||
String generate();
|
||||
|
||||
String color(String tokenName) {
|
||||
final String tokenColor = '$tokenName.color';
|
||||
final String tokenOpacity = '$tokenName.opacity';
|
||||
String value = '${tokens[tokenColor]!}';
|
||||
if (tokens.containsKey(tokenOpacity)) {
|
||||
final String opacity = tokens[tokens[tokenOpacity]!]!.toString();
|
||||
value += '.withOpacity($opacity)';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
String elevation(String tokenName) {
|
||||
final String elevationName = '$tokenName.elevation';
|
||||
final Map<String, dynamic> elevationValue = tokens[tokens[elevationName]!]! as Map<String, dynamic>;
|
||||
return elevationValue['value']!.toString();
|
||||
}
|
||||
|
||||
String shape(String tokenName) {
|
||||
// TODO(darrenaustin): handle more than just rounded rectangle shapes
|
||||
final String shapeToken = tokens[tokenName]! as String;
|
||||
final Map<String, dynamic> shape = tokens[shapeToken]! as Map<String, dynamic>;
|
||||
final Map<String, dynamic> shapeValue = shape['value']! as Map<String, dynamic>;
|
||||
return 'const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(${shapeValue['value']!})))';
|
||||
}
|
||||
|
||||
String value(String tokenName) {
|
||||
final Map<String, dynamic> value = tokens[tokenName]! as Map<String, dynamic>;
|
||||
return value['value'].toString();
|
||||
}
|
||||
|
||||
String textStyle(String tokenName) {
|
||||
final String fontName = '$tokenName.font';
|
||||
return tokens[fontName]!.toString();
|
||||
}
|
||||
}
|
58
dev/tools/gen_defaults/pubspec.yaml
Normal file
58
dev/tools/gen_defaults/pubspec.yaml
Normal file
@ -0,0 +1,58 @@
|
||||
name: gen_defaults
|
||||
description: A command line script to generate Material component defaults from the token database.
|
||||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0-0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
|
||||
async: 2.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
charcode: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.15.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
http_parser: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
source_span: 1.8.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
string_scanner: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
term_glyph: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
typed_data: 1.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
|
||||
dev_dependencies:
|
||||
path: 1.8.0
|
||||
test: 1.19.5
|
||||
|
||||
_fe_analyzer_shared: 31.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
analyzer: 2.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
cli_util: 0.3.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 6.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
frontend_server_client: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 2.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
http_multi_server: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
js: 0.6.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
logging: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
matcher: 0.12.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
mime: 1.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
package_config: 2.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
pool: 1.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
pub_semver: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf: 1.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_packages_handler: 3.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_static: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_web_socket: 1.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
source_map_stack_trace: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
source_maps: 0.10.10 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
stack_trace: 1.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
stream_channel: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
test_core: 0.4.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
vm_service: 7.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
watcher: 1.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
web_socket_channel: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
webkit_inspection_protocol: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
yaml: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
|
||||
# PUBSPEC CHECKSUM: aa20
|
110
dev/tools/gen_defaults/test/gen_defaults_test.dart
Normal file
110
dev/tools/gen_defaults/test/gen_defaults_test.dart
Normal file
@ -0,0 +1,110 @@
|
||||
// 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 'dart:io';
|
||||
|
||||
import 'package:gen_defaults/template.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('Templates will append to the end of a file', () {
|
||||
final Directory tempDir = Directory.systemTemp.createTempSync('gen_defaults');
|
||||
try {
|
||||
// Create a temporary file with some content.
|
||||
final File tempFile = File(path.join(tempDir.path, 'test_template.txt'));
|
||||
tempFile.createSync();
|
||||
tempFile.writeAsStringSync('''
|
||||
// This is a file with stuff in it.
|
||||
// This part shouldn't be changed by
|
||||
// the template.
|
||||
''');
|
||||
|
||||
// Have a test template append new parameterized content to the end of
|
||||
// the file.
|
||||
final Map<String, dynamic> tokens = <String, dynamic>{'foo': 'Foobar', 'bar': 'Barfoo'};
|
||||
TestTemplate(tempFile.path, tokens).updateFile();
|
||||
|
||||
expect(tempFile.readAsStringSync(), '''
|
||||
// This is a file with stuff in it.
|
||||
// This part shouldn't be changed by
|
||||
// the template.
|
||||
|
||||
// BEGIN GENERATED TOKEN PROPERTIES
|
||||
|
||||
// Generated code to the end of this file. Do not edit by hand.
|
||||
// These defaults are generated from the Material Design Token
|
||||
// database by the script dev/tools/gen_defaults/bin/gen_defaults.dart.
|
||||
|
||||
static final String tokenFoo = 'Foobar';
|
||||
static final String tokenBar = 'Barfoo';
|
||||
|
||||
// END GENERATED TOKEN PROPERTIES
|
||||
''');
|
||||
|
||||
} finally {
|
||||
tempDir.deleteSync(recursive: true);
|
||||
}
|
||||
});
|
||||
|
||||
test('Templates will update over previously generated code at the end of a file', () {
|
||||
final Directory tempDir = Directory.systemTemp.createTempSync('gen_defaults');
|
||||
try {
|
||||
// Create a temporary file with some content.
|
||||
final File tempFile = File(path.join(tempDir.path, 'test_template.txt'));
|
||||
tempFile.createSync();
|
||||
tempFile.writeAsStringSync('''
|
||||
// This is a file with stuff in it.
|
||||
// This part shouldn't be changed by
|
||||
// the template.
|
||||
|
||||
// BEGIN GENERATED TOKEN PROPERTIES
|
||||
|
||||
// Generated code to the end of this file. Do not edit by hand.
|
||||
// These defaults are generated from the Material Design Token
|
||||
// database by the script dev/tools/gen_defaults/bin/gen_defaults.dart.
|
||||
|
||||
static final String tokenFoo = 'Foobar';
|
||||
static final String tokenBar = 'Barfoo';
|
||||
|
||||
// END GENERATED TOKEN PROPERTIES
|
||||
''');
|
||||
|
||||
// Have a test template append new parameterized content to the end of
|
||||
// the file.
|
||||
final Map<String, dynamic> tokens = <String, dynamic>{'foo': 'foo', 'bar': 'bar'};
|
||||
TestTemplate(tempFile.path, tokens).updateFile();
|
||||
|
||||
expect(tempFile.readAsStringSync(), '''
|
||||
// This is a file with stuff in it.
|
||||
// This part shouldn't be changed by
|
||||
// the template.
|
||||
|
||||
// BEGIN GENERATED TOKEN PROPERTIES
|
||||
|
||||
// Generated code to the end of this file. Do not edit by hand.
|
||||
// These defaults are generated from the Material Design Token
|
||||
// database by the script dev/tools/gen_defaults/bin/gen_defaults.dart.
|
||||
|
||||
static final String tokenFoo = 'foo';
|
||||
static final String tokenBar = 'bar';
|
||||
|
||||
// END GENERATED TOKEN PROPERTIES
|
||||
''');
|
||||
|
||||
} finally {
|
||||
tempDir.deleteSync(recursive: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class TestTemplate extends TokenTemplate {
|
||||
TestTemplate(String fileName, Map<String, dynamic> tokens) : super(fileName, tokens);
|
||||
|
||||
@override
|
||||
String generate() => '''
|
||||
static final String tokenFoo = '${tokens['foo']}';
|
||||
static final String tokenBar = '${tokens['bar']}';
|
||||
''';
|
||||
}
|
Loading…
Reference in New Issue
Block a user