Files

126 lines
3.3 KiB
Dart

import 'package:flutter/material.dart';
import 'custom_font_service.dart';
const String kDisplayFontFamily = 'Outfit';
@immutable
class AppDisplayFont extends ThemeExtension<AppDisplayFont> {
final String? family;
const AppDisplayFont(this.family);
@override
AppDisplayFont copyWith({String? family}) =>
AppDisplayFont(family ?? this.family);
@override
AppDisplayFont lerp(ThemeExtension<AppDisplayFont>? other, double t) =>
t < 0.5 ? this : (other as AppDisplayFont? ?? this);
}
String? displayFontOf(BuildContext context) =>
Theme.of(context).extension<AppDisplayFont>()?.family ?? kDisplayFontFamily;
class AppFont {
final String id;
final String label;
final String? fontFamily;
final double metricScale;
const AppFont({
required this.id,
required this.label,
this.fontFamily,
this.metricScale = 1.0,
});
bool get isSystem => fontFamily == null;
bool get isCustom => id.startsWith(AppFonts.customPrefix);
}
class AppFonts {
static const String prefKey = 'app_font';
static const String scalePrefKey = 'app_font_scale';
static const String customPrefix = 'g:';
static const double minScale = 0.60;
static const double maxScale = 1.35;
static const double defaultScale = 1.0;
static const List<AppFont> builtIn = [
AppFont(id: 'system', label: 'Системный'),
AppFont(
id: 'inter',
label: 'Inter',
fontFamily: 'Inter',
metricScale: 0.967,
),
AppFont(
id: 'unbounded',
label: 'Unbounded',
fontFamily: 'Unbounded',
metricScale: 0.933,
),
];
static AppFont get fallback => builtIn.first;
static String customId(String family) => '$customPrefix$family';
static AppFont resolve(String id) {
if (id.startsWith(customPrefix)) {
final family = id.substring(customPrefix.length);
return AppFont(
id: id,
label: family,
fontFamily: family,
metricScale: CustomFontService.metricScaleFor(family),
);
}
return builtIn.firstWhere((f) => f.id == id, orElse: () => fallback);
}
static double effectiveScale(String id, double userScale) =>
userScale * resolve(id).metricScale;
static String? displayFamily(String id) {
final font = resolve(id);
return font.isSystem ? kDisplayFontFamily : font.fontFamily;
}
static TextTheme textTheme(String id, TextTheme base) {
final family = resolve(id).fontFamily;
if (family == null) return base;
return base.apply(fontFamily: family);
}
static TextStyle sample(String id, {required double fontSize}) {
final family = resolve(id).fontFamily;
return TextStyle(fontFamily: family, fontSize: fontSize);
}
static double clampScale(double scale) =>
scale.clamp(minScale, maxScale).toDouble();
static String? familyFromInput(String input) {
var value = input.trim();
if (value.isEmpty) return null;
final uri = Uri.tryParse(value);
if (uri != null && uri.host.contains('fonts.google.com')) {
final idx = uri.pathSegments.indexOf('specimen');
if (idx != -1 && idx + 1 < uri.pathSegments.length) {
value = uri.pathSegments[idx + 1];
}
}
try {
value = Uri.decodeComponent(value);
} catch (_) {}
value = value.replaceAll('+', ' ').trim();
return value.isEmpty ? null : value;
}
}