Skip to content

Commit 109e65a

Browse files
committed
feat: add wechat login
1 parent 4f54309 commit 109e65a

15 files changed

Lines changed: 225 additions & 17 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
## 🚀 项目简介
1010

11-
FlutterTDGetx Template 是一个 Flutter 项目模板,整合了常用的技术栈和开发实践。该模板旨在帮助开发者快速搭建项目结构,减少重复的配置工作。
11+
Flutter Template 是一个 Flutter 项目模板,整合了常用的技术栈和开发实践。该模板旨在帮助开发者快速搭建项目结构,减少重复的配置工作。
1212

1313
### ✨ 主要特性
1414

android/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
3939
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
4040
<queries>
41+
<!-- WeChat -->
42+
<package android:name="com.tencent.mm" />
4143
<intent>
4244
<action android:name="android.intent.action.PROCESS_TEXT"/>
4345
<data android:mimeType="text/plain"/>

ios/Podfile.lock

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ PODS:
44
- Flutter (1.0.0)
55
- flutter_local_notifications (0.0.1):
66
- Flutter
7+
- fluwx (0.0.1):
8+
- Flutter
9+
- fluwx/pay (= 0.0.1)
10+
- fluwx/pay (0.0.1):
11+
- Flutter
12+
- WechatOpenSDK-XCFramework (~> 2.0.5)
713
- image_picker_ios (0.0.1):
814
- Flutter
915
- open_filex (0.0.2):
@@ -24,11 +30,13 @@ PODS:
2430
- FlutterMacOS
2531
- url_launcher_ios (0.0.1):
2632
- Flutter
33+
- WechatOpenSDK-XCFramework (2.0.5)
2734

2835
DEPENDENCIES:
2936
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
3037
- Flutter (from `Flutter`)
3138
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
39+
- fluwx (from `.symlinks/plugins/fluwx/ios`)
3240
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
3341
- open_filex (from `.symlinks/plugins/open_filex/ios`)
3442
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
@@ -39,13 +47,19 @@ DEPENDENCIES:
3947
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
4048
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
4149

50+
SPEC REPOS:
51+
trunk:
52+
- WechatOpenSDK-XCFramework
53+
4254
EXTERNAL SOURCES:
4355
device_info_plus:
4456
:path: ".symlinks/plugins/device_info_plus/ios"
4557
Flutter:
4658
:path: Flutter
4759
flutter_local_notifications:
4860
:path: ".symlinks/plugins/flutter_local_notifications/ios"
61+
fluwx:
62+
:path: ".symlinks/plugins/fluwx/ios"
4963
image_picker_ios:
5064
:path: ".symlinks/plugins/image_picker_ios/ios"
5165
open_filex:
@@ -69,6 +83,7 @@ SPEC CHECKSUMS:
6983
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
7084
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
7185
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
86+
fluwx: 95a2274c23418c1098940bd00dcfe7c975bb0550
7287
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
7388
open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4
7489
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
@@ -78,6 +93,7 @@ SPEC CHECKSUMS:
7893
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
7994
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
8095
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
96+
WechatOpenSDK-XCFramework: ff342ae616bb86df3d236aca38059dfd4bc4a949
8197

8298
PODFILE CHECKSUM: 4305caec6b40dde0ae97be1573c53de1882a07e5
8399

ios/Runner.xcodeproj/project.pbxproj

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@
152152
11591E77C21C9BBC30346FAA /* Pods-RunnerTests.release.xcconfig */,
153153
1ACC3DB9C3AF74E35299AF20 /* Pods-RunnerTests.profile.xcconfig */,
154154
);
155-
name = Pods;
156155
path = Pods;
157156
sourceTree = "<group>";
158157
};
@@ -489,14 +488,14 @@
489488
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
490489
CLANG_ENABLE_MODULES = YES;
491490
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
492-
DEVELOPMENT_TEAM = G59Z5P2Q5L;
491+
DEVELOPMENT_TEAM = 64K44D3D4A;
493492
ENABLE_BITCODE = NO;
494493
INFOPLIST_FILE = Runner/Info.plist;
495494
LD_RUNPATH_SEARCH_PATHS = (
496495
"$(inherited)",
497496
"@executable_path/Frameworks",
498497
);
499-
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTDGetxTemplate;
498+
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTemplate;
500499
PRODUCT_NAME = "$(TARGET_NAME)";
501500
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
502501
SWIFT_VERSION = 5.0;
@@ -513,7 +512,7 @@
513512
CURRENT_PROJECT_VERSION = 1;
514513
GENERATE_INFOPLIST_FILE = YES;
515514
MARKETING_VERSION = 1.0;
516-
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTDGetxTemplate.RunnerTests;
515+
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTemplate.RunnerTests;
517516
PRODUCT_NAME = "$(TARGET_NAME)";
518517
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
519518
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -531,7 +530,7 @@
531530
CURRENT_PROJECT_VERSION = 1;
532531
GENERATE_INFOPLIST_FILE = YES;
533532
MARKETING_VERSION = 1.0;
534-
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTDGetxTemplate.RunnerTests;
533+
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTemplate.RunnerTests;
535534
PRODUCT_NAME = "$(TARGET_NAME)";
536535
SWIFT_VERSION = 5.0;
537536
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -547,7 +546,7 @@
547546
CURRENT_PROJECT_VERSION = 1;
548547
GENERATE_INFOPLIST_FILE = YES;
549548
MARKETING_VERSION = 1.0;
550-
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTDGetxTemplate.RunnerTests;
549+
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTemplate.RunnerTests;
551550
PRODUCT_NAME = "$(TARGET_NAME)";
552551
SWIFT_VERSION = 5.0;
553552
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -672,14 +671,14 @@
672671
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
673672
CLANG_ENABLE_MODULES = YES;
674673
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
675-
DEVELOPMENT_TEAM = G59Z5P2Q5L;
674+
DEVELOPMENT_TEAM = 64K44D3D4A;
676675
ENABLE_BITCODE = NO;
677676
INFOPLIST_FILE = Runner/Info.plist;
678677
LD_RUNPATH_SEARCH_PATHS = (
679678
"$(inherited)",
680679
"@executable_path/Frameworks",
681680
);
682-
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTDGetxTemplate;
681+
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTemplate;
683682
PRODUCT_NAME = "$(TARGET_NAME)";
684683
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
685684
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -695,14 +694,14 @@
695694
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
696695
CLANG_ENABLE_MODULES = YES;
697696
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
698-
DEVELOPMENT_TEAM = G59Z5P2Q5L;
697+
DEVELOPMENT_TEAM = 64K44D3D4A;
699698
ENABLE_BITCODE = NO;
700699
INFOPLIST_FILE = Runner/Info.plist;
701700
LD_RUNPATH_SEARCH_PATHS = (
702701
"$(inherited)",
703702
"@executable_path/Frameworks",
704703
);
705-
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTDGetxTemplate;
704+
PRODUCT_BUNDLE_IDENTIFIER = com.rain.fluttertdkit.flutterTemplate;
706705
PRODUCT_NAME = "$(TARGET_NAME)";
707706
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
708707
SWIFT_VERSION = 5.0;

ios/Runner/Info.plist

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,30 @@
4545
<true/>
4646
<key>UIApplicationSupportsIndirectInputEvents</key>
4747
<true/>
48+
<key>LSApplicationQueriesSchemes</key>
49+
<array>
50+
<string>weixin</string>
51+
<string>wechat</string>
52+
<string>weixinULAPI</string>
53+
<string>weixinURLParamsAPI</string>
54+
</array>
55+
<key>CFBundleURLTypes</key>
56+
<array>
57+
<dict>
58+
<key>CFBundleTypeRole</key>
59+
<string>Editor</string>
60+
<key>CFBundleURLName</key>
61+
<string>weixin</string>
62+
<key>CFBundleURLSchemes</key>
63+
<array>
64+
<string>wx_placeholder_appid</string>
65+
</array>
66+
</dict>
67+
</array>
68+
<key>NSAppTransportSecurity</key>
69+
<dict>
70+
<key>NSAllowsArbitraryLoads</key>
71+
<true/>
72+
</dict>
4873
</dict>
4974
</plist>

lib/application.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:flutter_template/res/json_res.dart';
77
import 'dart:convert';
88

99
import 'core/util/storage/storage_util.dart';
10+
import 'core/util/wechat/wechat_util.dart';
1011

1112
class Application {
1213
/// 主题 - 使用响应式变量 (Material 3)
@@ -25,6 +26,9 @@ class Application {
2526
/// 初始化全局存储
2627
await StorageUtil.init();
2728

29+
/// 初始化微信 SDK
30+
await WechatUtil.init();
31+
2832
/// 仅在调试模式下初始化 Alice
2933
initAlice();
3034

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import 'dart:async';
2+
import 'package:fluwx/fluwx.dart';
3+
import '../log/log_util.dart';
4+
import '../toast/toast_util.dart';
5+
6+
/// 微信工具类
7+
class WechatUtil {
8+
// TODO: 替换为真实的 App ID
9+
static const String _appId = "wx_placeholder_appid";
10+
// TODO: 替换为真实的 Universal Link
11+
static const String _universalLink = "https://your.universal.link/";
12+
13+
static final Fluwx _fluwx = Fluwx();
14+
15+
/// 初始化微信 SDK
16+
static Future<void> init() async {
17+
await _fluwx.registerApi(
18+
appId: _appId,
19+
doOnAndroid: true,
20+
doOnIOS: true,
21+
universalLink: _universalLink,
22+
);
23+
LogUtil.d("WeChat SDK initialized");
24+
}
25+
26+
/// 检查是否安装微信
27+
static Future<bool> isInstalled() async {
28+
return await _fluwx.isWeChatInstalled;
29+
}
30+
31+
/// 发起微信登录
32+
/// 返回 code,如果失败则返回 null
33+
static Future<String?> login() async {
34+
bool installed = await isInstalled();
35+
if (!installed) {
36+
ToastUtil.show("请先安装微信");
37+
return null;
38+
}
39+
40+
final Completer<String?> completer = Completer<String?>();
41+
42+
// 监听响应
43+
final cancelable = _fluwx.addSubscriber((response) {
44+
if (response is WeChatAuthResponse) {
45+
if (response.errCode == 0) {
46+
LogUtil.d("WeChat login success, code: ${response.code}");
47+
if (!completer.isCompleted) {
48+
completer.complete(response.code);
49+
}
50+
} else {
51+
LogUtil.e("WeChat login failed: ${response.errCode} ${response.errStr}");
52+
if (!completer.isCompleted) {
53+
completer.complete(null);
54+
}
55+
}
56+
}
57+
});
58+
59+
try {
60+
final result = await _fluwx.authBy(which: NormalAuth(
61+
scope: "snsapi_userinfo",
62+
state: "wechat_sdk_demo_test",
63+
));
64+
65+
if (!result) {
66+
ToastUtil.show("拉起微信失败");
67+
if (!completer.isCompleted) completer.complete(null);
68+
}
69+
} catch (e) {
70+
LogUtil.e("WeChat auth exception: $e");
71+
if (!completer.isCompleted) completer.complete(null);
72+
}
73+
74+
// 设置超时
75+
return completer.future.timeout(const Duration(minutes: 2), onTimeout: () {
76+
LogUtil.w("WeChat login timeout");
77+
return null;
78+
}).whenComplete(() {
79+
cancelable.cancel();
80+
});
81+
}
82+
}

lib/feature/auth/controllers/login_controller.dart

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ import 'package:flutter_template/core/base/base/base_logic.dart';
66
import 'package:flutter_template/core/network/service/auth/auth_api.dart';
77
import 'package:flutter_template/core/model/request/send_sms_request.dart';
88
import 'package:flutter_template/core/model/request/sms_login_request.dart';
9+
import 'package:flutter_template/core/ui/dialog/loading_dialog.dart';
10+
import 'package:flutter_template/core/util/wechat/wechat_util.dart';
11+
import 'package:flutter_template/core/util/toast/toast_util.dart';
12+
import 'package:flutter_template/core/util/log/log_util.dart';
13+
import 'package:flutter_template/routes/app_pages.dart';
914

1015
class LoginController extends BaseLogic {
1116
final AuthApi _authApi = Get.find<AuthApi>();
@@ -106,9 +111,23 @@ class LoginController extends BaseLogic {
106111
final result = await _authApi.loginBySms(request);
107112
if (result.code == 1000) {
108113
// showToast('登录成功');
109-
// TODO: 跳转到主页
114+
Get.offAllNamed(Routes.MAIN);
110115
} else {
111116
// showToast(result.message ?? '登录失败');
112117
}
113118
}
119+
120+
/// 微信登录
121+
Future<void> loginWithWechat() async {
122+
LoadingDialog.show();
123+
final code = await WechatUtil.login();
124+
LoadingDialog.hide();
125+
126+
if (code != null) {
127+
LogUtil.d("微信登录 Code: $code");
128+
ToastUtil.success("微信授权成功");
129+
// 模拟登录成功,跳转主页
130+
Get.offAllNamed(Routes.MAIN);
131+
}
132+
}
114133
}

lib/feature/auth/views/login_view.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class LoginView extends GetView<LoginController> {
3636
_buildSmsCodeInput(context),
3737
const SizedBox(height: 48),
3838
_buildLoginButton(),
39+
const SizedBox(height: 40),
40+
_buildWechatLoginButton(),
3941
],
4042
),
4143
),
@@ -44,6 +46,49 @@ class LoginView extends GetView<LoginController> {
4446
);
4547
}
4648

49+
Widget _buildWechatLoginButton() {
50+
return Center(
51+
child: Column(
52+
children: [
53+
Row(
54+
children: [
55+
const Expanded(child: Divider(color: Color(0xFFEEEEEE))),
56+
Padding(
57+
padding: const EdgeInsets.symmetric(horizontal: 16),
58+
child: Text(
59+
'其他登录方式',
60+
style: TextStyle(
61+
fontSize: 14,
62+
color: const Color(0xFF999999),
63+
),
64+
),
65+
),
66+
const Expanded(child: Divider(color: Color(0xFFEEEEEE))),
67+
],
68+
),
69+
const SizedBox(height: 24),
70+
InkWell(
71+
onTap: controller.loginWithWechat,
72+
child: Container(
73+
width: 48,
74+
height: 48,
75+
decoration: const BoxDecoration(
76+
shape: BoxShape.circle,
77+
color: Color(0xFF00C800), // 微信绿
78+
),
79+
alignment: Alignment.center,
80+
child: const Icon(
81+
Icons.wechat,
82+
color: Colors.white,
83+
size: 28,
84+
),
85+
),
86+
),
87+
],
88+
),
89+
);
90+
}
91+
4792
Widget _buildLabel(String text) {
4893
return Text(
4994
text,

0 commit comments

Comments
 (0)