Skip to content

Commit 3a04133

Browse files
authored
chore: ui component (#10)
* chore: ui * feat: add ci cd * chore: ci
1 parent 742e07d commit 3a04133

52 files changed

Lines changed: 1042 additions & 816 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: Flutter CI/CD
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build-android:
11+
name: Build Android APK
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v3
16+
17+
- name: Set up Flutter
18+
uses: subosito/flutter-action@v2
19+
with:
20+
channel: stable
21+
22+
- name: Install dependencies
23+
run: flutter pub get
24+
25+
- name: Analyze code
26+
run: flutter analyze
27+
28+
- name: Run tests
29+
run: flutter test
30+
31+
- name: Build APK
32+
run: flutter build apk --release
33+
34+
- name: Upload artifact
35+
uses: actions/upload-artifact@v4
36+
with:
37+
name: android-apk
38+
path: build/app/outputs/flutter-apk/app-release.apk
39+
40+
- name: Distribute APK via Firebase
41+
run: |
42+
firebase appdistribution:distribute build/app/outputs/flutter-apk/app-release.apk \
43+
--app ${{ secrets.FIREBASE_APP_ID }} \
44+
--groups testers \
45+
--token ${{ secrets.FIREBASE_TOKEN }}
46+
47+
build-ios:
48+
name: Build iOS for TestFlight
49+
runs-on: macos-latest
50+
needs: build-android
51+
52+
steps:
53+
- uses: actions/checkout@v3
54+
55+
- name: Set up Flutter
56+
uses: subosito/flutter-action@v2
57+
with:
58+
channel: stable
59+
60+
- name: Install dependencies
61+
run: flutter pub get
62+
63+
- name: Build iOS
64+
run: flutter build ios --release --no-codesign
65+
66+
- name: Setup Ruby
67+
uses: ruby/setup-ruby@v1
68+
with:
69+
ruby-version: '3.2'
70+
71+
- name: Install Fastlane
72+
run: gem install fastlane
73+
74+
- name: Fastlane Match Setup
75+
run: |
76+
bundle exec fastlane match appstore
77+
env:
78+
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
79+
GIT_AUTHORIZATION: ${{ secrets.MATCH_GIT_AUTH }}
80+
81+
- name: Deploy to TestFlight
82+
run: |
83+
cd ios
84+
fastlane beta
85+
env:
86+
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
87+
APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
88+
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22
"cSpell.words": [
33
"appbundle",
44
"appcast",
5+
"appdistribution",
6+
"appstore",
7+
"codesign",
58
"fluttertdkit",
69
"Getx",
710
"MVVM",
811
"screenutil",
9-
"tdesign",
12+
"subosito",
1013
"tdkit",
1114
"upgrader"
1215
]

README.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
# Flutter TDesign Getx Template
1+
# Flutter Template
22

3-
> 基于 TDesign + GetX 的 Flutter 项目模板,整合常用功能和开发实践
3+
> Flutter 项目模板,整合常用功能和开发实践
44
55
- [Flutter](https://flutter.dev/)
6-
- [TDesign](https://tdesign.tencent.com/flutter/overview)
76
- [GetX](https://chornthorn.github.io/getx-docs/docs)
87
- [DeepWiki](https://deepwiki.com/you-want/flutter-td-getx-template)
98

@@ -14,7 +13,6 @@ FlutterTDGetx Template 是一个 Flutter 项目模板,整合了常用的技术
1413
### ✨ 主要特性
1514

1615
- **🎯 常用架构**: GetX 推荐的响应式架构
17-
- **🎨 UI 组件**: 集成腾讯 TDesign 组件库
1816
- **🌐 网络请求**: 封装了常用的网络请求功能
1917
- **📱 屏幕适配**: 内置屏幕适配解决方案
2018
- **🔧 开发工具**: 集成了代码生成、调试等常用工具
@@ -24,7 +22,6 @@ FlutterTDGetx Template 是一个 Flutter 项目模板,整合了常用的技术
2422
### 🛠 技术栈
2523

2624
- **状态管理**: GetX 4.x
27-
- **UI 框架**: TDesign Flutter
2825
- **网络请求**: Dio + Retrofit
2926
- **数据序列化**: son_annotation + json_serializable
3027
- **屏幕适配**: Flutter ScreenUtil

lib/application.dart

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import 'package:flutter/foundation.dart';
33
import 'package:flutter/material.dart';
44
import 'package:flutter/services.dart';
55
import 'package:get/get.dart';
6-
import 'package:flutter_td_getx_template/res/json_res.dart';
7-
import 'package:tdesign_flutter/tdesign_flutter.dart';
6+
import 'package:flutter_template/res/json_res.dart';
7+
import 'dart:convert';
88

99
import 'core/util/storage/storage_util.dart';
1010

1111
class Application {
12-
/// 主题 - 使用响应式变量
13-
static final Rx<TDThemeData> themeData = TDThemeData.defaultData().obs;
12+
/// 主题 - 使用响应式变量 (Material 3)
13+
static final Rx<ThemeData> themeData = _themeFromSeed(const Color(0xFF2f46ec)).obs;
1414

1515
/// Alice 网络请求调试工具
1616
static late Alice alice;
@@ -52,17 +52,67 @@ class Application {
5252

5353
/// 设置主题样式
5454
static setTheme() async {
55-
// 主题配置
56-
TDTheme.needMultiTheme();
57-
var jsonString = await rootBundle.loadString(JsonRes.theme);
58-
themeData.value = TDThemeData.fromJson('theme', jsonString)!;
55+
final jsonString = await rootBundle.loadString(JsonRes.theme);
56+
final seed = _extractBrandColor7(jsonString, 'theme') ?? const Color(0xFF2f46ec);
57+
themeData.value = _themeFromSeed(seed);
5958
}
6059

6160
/// 更新主题
62-
static void updateTheme(TDThemeData newTheme) {
61+
static void updateTheme(ThemeData newTheme) {
6362
themeData.value = newTheme;
6463
}
6564

65+
/// 提供外部调用的种子主题构建
66+
static ThemeData themeFromSeed(Color seed) => _themeFromSeed(seed);
67+
68+
/// 从 JSON 中提取 brandColor7 作为种子色
69+
static Color? _extractBrandColor7(String jsonString, String key) {
70+
try {
71+
final Map<String, dynamic> data = json.decode(jsonString) as Map<String, dynamic>;
72+
final Map<String, dynamic>? section = data[key] as Map<String, dynamic>?;
73+
if (section == null) return null;
74+
final Map<String, dynamic>? color = section['color'] as Map<String, dynamic>?;
75+
final String? hex = color?['brandColor7'] as String?;
76+
if (hex == null) return null;
77+
return _colorFromHex(hex);
78+
} catch (_) {
79+
return null;
80+
}
81+
}
82+
83+
static ThemeData _themeFromSeed(Color seed) {
84+
final colorScheme = ColorScheme.fromSeed(seedColor: seed, brightness: Brightness.light);
85+
return ThemeData(
86+
useMaterial3: true,
87+
colorScheme: colorScheme,
88+
appBarTheme: AppBarTheme(
89+
backgroundColor: colorScheme.surface,
90+
foregroundColor: colorScheme.onSurface,
91+
elevation: 0,
92+
centerTitle: true,
93+
),
94+
navigationBarTheme: NavigationBarThemeData(
95+
backgroundColor: colorScheme.surface,
96+
indicatorColor: colorScheme.secondaryContainer,
97+
iconTheme: WidgetStateProperty.all(IconThemeData(color: colorScheme.onSurfaceVariant)),
98+
labelTextStyle: WidgetStateProperty.all(TextStyle(color: colorScheme.onSurfaceVariant)),
99+
),
100+
filledButtonTheme: FilledButtonThemeData(
101+
style: FilledButton.styleFrom(
102+
backgroundColor: colorScheme.primary,
103+
foregroundColor: colorScheme.onPrimary,
104+
),
105+
),
106+
);
107+
}
108+
109+
static Color _colorFromHex(String hex) {
110+
final buffer = StringBuffer();
111+
if (hex.length == 6 || hex.length == 7) buffer.write('ff');
112+
buffer.write(hex.replaceFirst('#', ''));
113+
return Color(int.parse(buffer.toString(), radix: 16));
114+
}
115+
66116
/// 初始化 Alice
67117
static initAlice() {
68118
// 仅在调试模式下初始化 Alice

lib/core/base/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
- 页面状态和生命周期管理
5252

5353
### 2. 页面结构
54-
- 统一的导航栏实现(基于 TDesign)
54+
- 统一的导航栏实现
5555
- 可配置的页面布局
5656
- 状态保持支持
5757
- 底部导航和悬浮按钮支持

lib/core/base/base/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class MyLogic extends BaseLogic {
3030
### base_view.dart
3131
基础视图类,提供:
3232
- 页面基础结构定义
33-
- 导航栏配置(基于 TDesign)
33+
- 导航栏配置
3434
- 状态保持支持
3535
- 底部导航和悬浮按钮支持
3636
- 继承自 GetView
@@ -89,7 +89,7 @@ class MyView extends BaseView<MyLogic> {
8989
- 注意状态更新时机
9090

9191
2. 导航栏使用
92-
- 使用 TDesign 的导航栏组件
92+
- 使用导航栏组件
9393
- 合理配置导航栏属性
9494
- 注意返回按钮处理
9595

lib/core/base/base/base_view.dart

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'package:flutter/material.dart';
22
import 'package:get/get.dart';
3-
import 'package:tdesign_flutter/tdesign_flutter.dart';
43

54
import '../../design_system/widgets/keep_alive_wrapper.dart';
65

@@ -20,7 +19,7 @@ abstract class BaseView<T> extends GetView<T> {
2019
final Widget? navBottomWidget = null;
2120

2221
/// 设置右边按钮数组
23-
final List<TDNavBarItem>? rightBarItems = null;
22+
final List<Widget>? rightBarItems = null;
2423

2524
/// 是否隐藏导航栏
2625
final bool isHiddenNav = false;
@@ -43,19 +42,21 @@ abstract class BaseView<T> extends GetView<T> {
4342
/// 导航栏 子类可根据需求重写
4443
PreferredSizeWidget? head() {
4544
if (isHiddenNav) return null;
46-
return TDNavBar(
47-
leftBarItems:
48-
navBackBtn
49-
? useDefaultBack
50-
? [TDNavBarItem(iconWidget: const BackButton())]
51-
: null
52-
: null,
53-
padding: EdgeInsets.zero,
54-
title: navTitle,
55-
height: navHeight,
56-
useDefaultBack: false,
57-
rightBarItems: rightBarItems,
58-
belowTitleWidget: navBottomWidget,
45+
return AppBar(
46+
leading: navBackBtn
47+
? (useDefaultBack ? const BackButton() : null)
48+
: null,
49+
title: navTitle != null ? Text(navTitle!) : null,
50+
toolbarHeight: navHeight,
51+
automaticallyImplyLeading: false,
52+
centerTitle: true,
53+
actions: rightBarItems,
54+
bottom: navBottomWidget == null
55+
? null
56+
: PreferredSize(
57+
preferredSize: Size.fromHeight(navHeight * 0.4),
58+
child: navBottomWidget!,
59+
),
5960
);
6061
}
6162

lib/core/base/base_dialog/base_dialog.dart

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:get/get.dart';
33
import 'package:styled_widget/styled_widget.dart';
4-
import 'package:tdesign_flutter/tdesign_flutter.dart';
54

65
import '../../util/common/common_util.dart';
76
import '../../util/size/size_util.dart';
@@ -69,34 +68,33 @@ abstract class BaseDialog extends StatelessWidget {
6968
final maxHeight = screenHeight - statusBarHeight - safeAreaHeight;
7069

7170
Widget dialogContent = [
72-
TDNavBar(
73-
title: title,
74-
screenAdaptation: false,
75-
useDefaultBack: false,
71+
// 顶部标题与关闭按钮(Material 实现)
72+
SizedBox(
7673
height: 56,
77-
belowTitleWidget: const TDDivider(),
78-
rightBarItems:
79-
close
80-
? [
81-
TDNavBarItem(
82-
iconWidget: Icon(
83-
Icons.close,
84-
size: 28,
85-
// color: ConstantTheme.grayOpacity4,
86-
)
87-
.ripple()
88-
.clipRRect(all: 4)
89-
.gestures(
90-
onTap: () => Navigator.of(Get.context!).pop(),
91-
),
92-
),
93-
]
94-
: null,
74+
child: Stack(
75+
children: [
76+
Center(
77+
child: Text(
78+
title,
79+
style: Theme.of(context).textTheme.titleMedium,
80+
),
81+
),
82+
if (close)
83+
Positioned(
84+
right: 8,
85+
top: 0,
86+
bottom: 0,
87+
child: IconButton(
88+
icon: const Icon(Icons.close, size: 28),
89+
onPressed: () => Navigator.of(Get.context!).pop(),
90+
),
91+
),
92+
],
93+
),
9594
),
95+
if (line) const Divider(height: 1),
9696
body()
9797
.toColumn(
98-
// crossAxisAlignment: Constant.crossStart,
99-
// mainAxisAlignment: Constant.mainStart,
10098
)
10199
.padding(all: 16),
102100
if (CommonUtil.isNotNull(bottom()))

0 commit comments

Comments
 (0)