更新 AuthApi 枚举以添加获取推流地址的 API 路径;重构 Auth 页面以优化输入控件和按钮布局;更新扫码页面以导航到录制页面。
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
enum AuthApi {
|
enum AuthApi {
|
||||||
/// 获取 token
|
/// 获取 token
|
||||||
getToken('/api/events/device/token');
|
getToken('/api/events/device/token'),
|
||||||
|
|
||||||
|
/// 获取推流地址
|
||||||
|
getSteamKey('api/events/device/stream/key');
|
||||||
|
|
||||||
final String path;
|
final String path;
|
||||||
const AuthApi(this.path);
|
const AuthApi(this.path);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class _AuthPageWidgetState extends ConsumerState<AuthPageWidget> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_controller = TextEditingController();
|
_controller = TextEditingController();
|
||||||
|
_controller?.text = '555';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -36,20 +37,22 @@ class _AuthPageWidgetState extends ConsumerState<AuthPageWidget> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 180.h),
|
SizedBox(height: 180.h),
|
||||||
Text('裁判工作台', style: TextStyle(fontSize: 50, color: Colors.black)),
|
AppText('裁判工作台', fontSize: 30.sp),
|
||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
Text('输入执裁口令', style: TextStyle(fontSize: 30, color: Colors.black)),
|
Text(
|
||||||
SizedBox(height: 20.h),
|
'输入执裁口令',
|
||||||
|
style: TextStyle(fontSize: 18.sp, color: Colors.black),
|
||||||
SizedBox(
|
|
||||||
width: 280.w,
|
|
||||||
height: 80.h,
|
|
||||||
child: AppTextField(initialValue: 'hhh', controller: _controller),
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20.w),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
AppTextField(controller: _controller),
|
||||||
|
SizedBox(height: 20.h),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 280.w,
|
width: double.maxFinite,
|
||||||
height: 80.h,
|
|
||||||
child: AppButton(
|
child: AppButton(
|
||||||
label: '确定',
|
label: '确定',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -66,13 +69,6 @@ class _AuthPageWidgetState extends ConsumerState<AuthPageWidget> {
|
|||||||
if (message != null && message.isNotEmpty) {
|
if (message != null && message.isNotEmpty) {
|
||||||
AppToast.show(message);
|
AppToast.show(message);
|
||||||
}
|
}
|
||||||
// return;
|
|
||||||
// AppNavigator.push(
|
|
||||||
// const WebviewPage(
|
|
||||||
// url:
|
|
||||||
// 'http://192.168.1.64:5173/#/pages/h5/score-entry?eventId=10237&scheduleId=113716750536806400&playerId=74300708945531007',
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
},
|
},
|
||||||
variant: AppButtonVariant.secondary,
|
variant: AppButtonVariant.secondary,
|
||||||
isLoading: authState.isLoading,
|
isLoading: authState.isLoading,
|
||||||
@@ -80,6 +76,9 @@ class _AuthPageWidgetState extends ConsumerState<AuthPageWidget> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:recording_tool/app/router/app_navigator.dart';
|
import 'package:recording_tool/app/router/app_navigator.dart';
|
||||||
|
import 'package:recording_tool/features/recording/pages/page_record.dart';
|
||||||
import 'package:recording_tool/features/scan_qrcode/pages/page_push_test.dart';
|
import 'package:recording_tool/features/scan_qrcode/pages/page_push_test.dart';
|
||||||
import 'package:recording_tool/shared/widgets/app_bar.dart';
|
import 'package:recording_tool/shared/widgets/app_bar.dart';
|
||||||
import 'package:recording_tool/shared/widgets/app_button.dart';
|
import 'package:recording_tool/shared/widgets/app_button.dart';
|
||||||
@@ -40,7 +41,8 @@ class _AuthPageWidgetState extends State<ScanQrCodePage> {
|
|||||||
if (result == null || result.isEmpty) return;
|
if (result == null || result.isEmpty) return;
|
||||||
debugPrint('扫码结果: $result');
|
debugPrint('扫码结果: $result');
|
||||||
Future.delayed(const Duration(milliseconds: 1)).then((_) {
|
Future.delayed(const Duration(milliseconds: 1)).then((_) {
|
||||||
AppNavigator.push(PushSteamTestWidget(rtmpUrl: result));
|
AppNavigator.push(const RecordingPage());
|
||||||
|
// AppNavigator.push(PushSteamTestWidget(rtmpUrl: result));
|
||||||
// AppNavigator.push(
|
// AppNavigator.push(
|
||||||
// const WebviewPage(url: 'https://www.dronex.cc/'),
|
// const WebviewPage(url: 'https://www.dronex.cc/'),
|
||||||
// );
|
// );
|
||||||
|
|||||||
@@ -0,0 +1,234 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// 全局文本尺寸语义。
|
||||||
|
///
|
||||||
|
/// 业务代码应优先选择语义化的 variant,而不是直接散落 `fontSize`。
|
||||||
|
/// 这样后续调整整套字号体系时,只需要维护这一处映射。
|
||||||
|
enum AppTextVariant {
|
||||||
|
display,
|
||||||
|
headline,
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
body,
|
||||||
|
bodySmall,
|
||||||
|
label,
|
||||||
|
caption,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 全局文本颜色语义。
|
||||||
|
///
|
||||||
|
/// tone 只表达“文本在界面中的语义角色”,具体颜色从当前 Theme 解析,
|
||||||
|
/// 避免页面直接依赖硬编码色值。
|
||||||
|
enum AppTextTone {
|
||||||
|
primary,
|
||||||
|
secondary,
|
||||||
|
tertiary,
|
||||||
|
inverse,
|
||||||
|
brand,
|
||||||
|
success,
|
||||||
|
warning,
|
||||||
|
danger,
|
||||||
|
disabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 应用级文本组件。
|
||||||
|
///
|
||||||
|
/// `AppText` 是对 Flutter `Text` 的轻量封装,目标是统一页面里的字号、
|
||||||
|
/// 字重、颜色和溢出策略,同时保留 `Text` 的常用能力。
|
||||||
|
///
|
||||||
|
/// 使用建议:
|
||||||
|
/// - 普通文案使用默认 `AppText('内容')`。
|
||||||
|
/// - 标题使用 `variant: AppTextVariant.title`。
|
||||||
|
/// - 错误、警告、成功等状态文案使用 `tone`,不要在业务里直接写颜色。
|
||||||
|
/// - 只有遇到一次性视觉细节时才传入 `style`、`fontSize` 或 `fontWeight`。
|
||||||
|
class AppText extends StatelessWidget {
|
||||||
|
const AppText(
|
||||||
|
this.data, {
|
||||||
|
super.key,
|
||||||
|
this.variant = AppTextVariant.body,
|
||||||
|
this.tone = AppTextTone.primary,
|
||||||
|
this.style,
|
||||||
|
this.color,
|
||||||
|
this.fontSize,
|
||||||
|
this.fontWeight,
|
||||||
|
this.height,
|
||||||
|
this.letterSpacing,
|
||||||
|
this.textAlign,
|
||||||
|
this.textDirection,
|
||||||
|
this.locale,
|
||||||
|
this.softWrap,
|
||||||
|
this.overflow,
|
||||||
|
this.maxLines,
|
||||||
|
this.semanticsLabel,
|
||||||
|
this.textWidthBasis,
|
||||||
|
this.textHeightBehavior,
|
||||||
|
this.textScaler,
|
||||||
|
this.selectionColor,
|
||||||
|
}) : textSpan = null;
|
||||||
|
|
||||||
|
/// 富文本构造器。
|
||||||
|
///
|
||||||
|
/// 用于同一段文案中存在局部强调、不同颜色或不同字重的场景。
|
||||||
|
/// 外层的 `variant`、`tone` 和通用排版参数仍会作为默认样式作用到 span。
|
||||||
|
const AppText.rich(
|
||||||
|
this.textSpan, {
|
||||||
|
super.key,
|
||||||
|
this.variant = AppTextVariant.body,
|
||||||
|
this.tone = AppTextTone.primary,
|
||||||
|
this.style,
|
||||||
|
this.color,
|
||||||
|
this.fontSize,
|
||||||
|
this.fontWeight,
|
||||||
|
this.height,
|
||||||
|
this.letterSpacing,
|
||||||
|
this.textAlign,
|
||||||
|
this.textDirection,
|
||||||
|
this.locale,
|
||||||
|
this.softWrap,
|
||||||
|
this.overflow,
|
||||||
|
this.maxLines,
|
||||||
|
this.semanticsLabel,
|
||||||
|
this.textWidthBasis,
|
||||||
|
this.textHeightBehavior,
|
||||||
|
this.textScaler,
|
||||||
|
this.selectionColor,
|
||||||
|
}) : data = null;
|
||||||
|
|
||||||
|
/// 普通文本内容。与 [textSpan] 二选一。
|
||||||
|
final String? data;
|
||||||
|
|
||||||
|
/// 富文本内容。与 [data] 二选一。
|
||||||
|
final InlineSpan? textSpan;
|
||||||
|
|
||||||
|
/// 文本尺寸和基础字重语义。
|
||||||
|
final AppTextVariant variant;
|
||||||
|
|
||||||
|
/// 文本颜色语义。
|
||||||
|
final AppTextTone tone;
|
||||||
|
|
||||||
|
/// 额外样式覆盖。优先级高于 variant 和 tone。
|
||||||
|
final TextStyle? style;
|
||||||
|
|
||||||
|
/// 显式颜色覆盖。优先级高于 tone 和 `style.color`。
|
||||||
|
final Color? color;
|
||||||
|
|
||||||
|
/// 一次性字号覆盖。常规场景优先使用 [variant]。
|
||||||
|
final double? fontSize;
|
||||||
|
|
||||||
|
/// 一次性字重覆盖。常规场景优先使用 [variant]。
|
||||||
|
final FontWeight? fontWeight;
|
||||||
|
|
||||||
|
/// 行高覆盖。
|
||||||
|
final double? height;
|
||||||
|
|
||||||
|
/// 字间距覆盖。
|
||||||
|
final double? letterSpacing;
|
||||||
|
|
||||||
|
final TextAlign? textAlign;
|
||||||
|
final TextDirection? textDirection;
|
||||||
|
final Locale? locale;
|
||||||
|
final bool? softWrap;
|
||||||
|
final TextOverflow? overflow;
|
||||||
|
final int? maxLines;
|
||||||
|
final String? semanticsLabel;
|
||||||
|
final TextWidthBasis? textWidthBasis;
|
||||||
|
final TextHeightBehavior? textHeightBehavior;
|
||||||
|
final TextScaler? textScaler;
|
||||||
|
final Color? selectionColor;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final effectiveStyle = _resolveStyle(context);
|
||||||
|
|
||||||
|
if (textSpan != null) {
|
||||||
|
return Text.rich(
|
||||||
|
textSpan!,
|
||||||
|
style: effectiveStyle,
|
||||||
|
textAlign: textAlign,
|
||||||
|
textDirection: textDirection,
|
||||||
|
locale: locale,
|
||||||
|
softWrap: softWrap,
|
||||||
|
overflow: overflow,
|
||||||
|
maxLines: maxLines,
|
||||||
|
semanticsLabel: semanticsLabel,
|
||||||
|
textWidthBasis: textWidthBasis,
|
||||||
|
textHeightBehavior: textHeightBehavior,
|
||||||
|
textScaler: textScaler,
|
||||||
|
selectionColor: selectionColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
data ?? '',
|
||||||
|
style: effectiveStyle,
|
||||||
|
textAlign: textAlign,
|
||||||
|
textDirection: textDirection,
|
||||||
|
locale: locale,
|
||||||
|
softWrap: softWrap,
|
||||||
|
overflow: overflow,
|
||||||
|
maxLines: maxLines,
|
||||||
|
semanticsLabel: semanticsLabel,
|
||||||
|
textWidthBasis: textWidthBasis,
|
||||||
|
textHeightBehavior: textHeightBehavior,
|
||||||
|
textScaler: textScaler,
|
||||||
|
selectionColor: selectionColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 合成最终样式。
|
||||||
|
///
|
||||||
|
/// 优先级从低到高:
|
||||||
|
/// 1. Theme 中的 TextTheme。
|
||||||
|
/// 2. `variant` 与 `tone` 对应的默认样式。
|
||||||
|
/// 3. 外部传入的 `style`。
|
||||||
|
/// 4. `color`、`fontSize`、`fontWeight` 等显式字段。
|
||||||
|
TextStyle _resolveStyle(BuildContext context) {
|
||||||
|
final baseStyle = _variantStyle(Theme.of(context).textTheme);
|
||||||
|
final toneStyle = baseStyle.copyWith(color: _toneColor(context));
|
||||||
|
final mergedStyle = style == null ? toneStyle : toneStyle.merge(style);
|
||||||
|
|
||||||
|
return mergedStyle.copyWith(
|
||||||
|
color: color ?? mergedStyle.color,
|
||||||
|
fontSize: fontSize ?? mergedStyle.fontSize,
|
||||||
|
fontWeight: fontWeight ?? mergedStyle.fontWeight,
|
||||||
|
height: height ?? mergedStyle.height,
|
||||||
|
letterSpacing: letterSpacing ?? mergedStyle.letterSpacing,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextStyle _variantStyle(TextTheme textTheme) {
|
||||||
|
return switch (variant) {
|
||||||
|
AppTextVariant.display =>
|
||||||
|
textTheme.displaySmall ?? const TextStyle(fontSize: 36),
|
||||||
|
AppTextVariant.headline =>
|
||||||
|
textTheme.headlineSmall ?? const TextStyle(fontSize: 24),
|
||||||
|
AppTextVariant.title =>
|
||||||
|
textTheme.titleMedium ?? const TextStyle(fontSize: 16),
|
||||||
|
AppTextVariant.subtitle =>
|
||||||
|
textTheme.titleSmall ?? const TextStyle(fontSize: 14),
|
||||||
|
AppTextVariant.body =>
|
||||||
|
textTheme.bodyMedium ?? const TextStyle(fontSize: 14),
|
||||||
|
AppTextVariant.bodySmall =>
|
||||||
|
textTheme.bodySmall ?? const TextStyle(fontSize: 12),
|
||||||
|
AppTextVariant.label =>
|
||||||
|
textTheme.labelLarge ?? const TextStyle(fontSize: 14),
|
||||||
|
AppTextVariant.caption =>
|
||||||
|
textTheme.labelSmall ?? const TextStyle(fontSize: 11),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _toneColor(BuildContext context) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return switch (tone) {
|
||||||
|
AppTextTone.primary => colors.onSurface,
|
||||||
|
AppTextTone.secondary => colors.onSurfaceVariant,
|
||||||
|
AppTextTone.tertiary => colors.outline,
|
||||||
|
AppTextTone.inverse => colors.onInverseSurface,
|
||||||
|
AppTextTone.brand => colors.primary,
|
||||||
|
AppTextTone.success => colors.tertiary,
|
||||||
|
AppTextTone.warning => colors.secondary,
|
||||||
|
AppTextTone.danger => colors.error,
|
||||||
|
AppTextTone.disabled => colors.onSurface.withValues(alpha: 0.38),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ export 'app_refresh_list.dart';
|
|||||||
export 'app_search_bar.dart';
|
export 'app_search_bar.dart';
|
||||||
export 'app_status_view.dart';
|
export 'app_status_view.dart';
|
||||||
export 'app_tag.dart';
|
export 'app_tag.dart';
|
||||||
|
export 'app_text.dart';
|
||||||
export 'app_text_field.dart';
|
export 'app_text_field.dart';
|
||||||
export 'app_toast.dart';
|
export 'app_toast.dart';
|
||||||
export 'safe_area_wrapper.dart';
|
export 'safe_area_wrapper.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user