mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
155 lines
5.4 KiB
Dart
155 lines
5.4 KiB
Dart
// 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, {
|
|
this.colorSchemePrefix = 'Theme.of(context).colorScheme.',
|
|
this.textThemePrefix = 'Theme.of(context).textTheme.'
|
|
});
|
|
|
|
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;
|
|
final String colorSchemePrefix;
|
|
final String textThemePrefix;
|
|
|
|
/// 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();
|
|
|
|
/// Generate a [ColorScheme] color name for the given token.
|
|
///
|
|
/// If there is a value for the given token, this will return
|
|
/// the value prepended with [colorSchemePrefix].
|
|
///
|
|
/// Otherwise it will return 'null'.
|
|
///
|
|
/// See also:
|
|
/// * [componentColor], that provides support for an optional opacity.
|
|
String color(String colorToken) {
|
|
return tokens.containsKey(colorToken)
|
|
? '$colorSchemePrefix${tokens[colorToken]}'
|
|
: 'null';
|
|
}
|
|
|
|
/// Generate a [ColorScheme] color name for the given component's color
|
|
/// with opacity if available.
|
|
///
|
|
/// If there is a value for the given component's color, this will return
|
|
/// the value prepended with [colorSchemePrefix]. If there is also
|
|
/// an opacity specified for the component, then the returned value
|
|
/// will include this opacity calculation.
|
|
///
|
|
/// If there is no value for the component's color, 'null' will be returned.
|
|
///
|
|
/// See also:
|
|
/// * [color], that provides support for looking up a raw color token.
|
|
String componentColor(String componentToken) {
|
|
final String colorToken = '$componentToken.color';
|
|
if (!tokens.containsKey(colorToken))
|
|
return 'null';
|
|
String value = color(colorToken);
|
|
final String opacityToken = '$componentToken.opacity';
|
|
if (tokens.containsKey(opacityToken)) {
|
|
value += '.withOpacity(${opacity(opacityToken)})';
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/// Generate the opacity value for the given token.
|
|
String? opacity(String token) {
|
|
final dynamic value = tokens[token];
|
|
if (value == null) {
|
|
return null;
|
|
}
|
|
if (value is double) {
|
|
return value.toString();
|
|
}
|
|
return tokens[value].toString();
|
|
}
|
|
|
|
/// Generate an elevation value for the given component token.
|
|
String elevation(String componentToken) {
|
|
return tokens[tokens['$componentToken.elevation']!]!.toString();
|
|
}
|
|
|
|
/// Generate a shape constant for the given component token.
|
|
///
|
|
/// Currently supports family:
|
|
/// - "SHAPE_FAMILY_ROUNDED_CORNERS" which maps to [RoundedRectangleBorder].
|
|
/// - "SHAPE_FAMILY_CIRCULAR" which maps to a [StadiumBorder].
|
|
String shape(String componentToken) {
|
|
final Map<String, dynamic> shape = tokens[tokens['$componentToken.shape']!]! as Map<String, dynamic>;
|
|
switch (shape['family']) {
|
|
case 'SHAPE_FAMILY_ROUNDED_CORNERS':
|
|
return 'const RoundedRectangleBorder(borderRadius: '
|
|
'BorderRadius.only('
|
|
'topLeft: Radius.circular(${shape['topLeft']}), '
|
|
'topRight: Radius.circular(${shape['topRight']}), '
|
|
'bottomLeft: Radius.circular(${shape['bottomLeft']}), '
|
|
'bottomRight: Radius.circular(${shape['bottomRight']})))';
|
|
case 'SHAPE_FAMILY_CIRCULAR':
|
|
return 'const StadiumBorder()';
|
|
}
|
|
print('Unsupported shape family type: ${shape['family']} for $componentToken');
|
|
return '';
|
|
}
|
|
|
|
/// Generate a [BorderSide] for the given component.
|
|
String border(String componentToken) {
|
|
if (!tokens.containsKey('$componentToken.color')) {
|
|
return 'null';
|
|
}
|
|
final String borderColor = componentColor(componentToken);
|
|
final double width = (tokens['$componentToken.width'] ?? 1.0) as double;
|
|
return 'BorderSide(color: $borderColor${width != 1.0 ? ", width: $width" : ""})';
|
|
}
|
|
|
|
/// Generate a [TextTheme] text style name for the given component token.
|
|
String textStyle(String componentToken) {
|
|
return '$textThemePrefix${tokens["$componentToken.text-style"]}';
|
|
}
|
|
}
|