35 changed files with 1616 additions and 35 deletions
@ -0,0 +1,36 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|||
<plist version="1.0"> |
|||
<dict> |
|||
<key>CLIENT_ID</key> |
|||
<string>907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278.apps.googleusercontent.com</string> |
|||
<key>REVERSED_CLIENT_ID</key> |
|||
<string>com.googleusercontent.apps.907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278</string> |
|||
<key>ANDROID_CLIENT_ID</key> |
|||
<string>907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com</string> |
|||
<key>API_KEY</key> |
|||
<string>AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808</string> |
|||
<key>GCM_SENDER_ID</key> |
|||
<string>907754492821</string> |
|||
<key>PLIST_VERSION</key> |
|||
<string>1</string> |
|||
<key>BUNDLE_ID</key> |
|||
<string>com.yunqiinnovation.deepsound</string> |
|||
<key>PROJECT_ID</key> |
|||
<string>deepsound-e0e3d</string> |
|||
<key>STORAGE_BUCKET</key> |
|||
<string>deepsound-e0e3d.firebasestorage.app</string> |
|||
<key>IS_ADS_ENABLED</key> |
|||
<false></false> |
|||
<key>IS_ANALYTICS_ENABLED</key> |
|||
<false></false> |
|||
<key>IS_APPINVITE_ENABLED</key> |
|||
<true></true> |
|||
<key>IS_GCM_ENABLED</key> |
|||
<true></true> |
|||
<key>IS_SIGNIN_ENABLED</key> |
|||
<true></true> |
|||
<key>GOOGLE_APP_ID</key> |
|||
<string>1:907754492821:ios:246794fc44b23174145c9c</string> |
|||
</dict> |
|||
</plist> |
|||
@ -0,0 +1,47 @@ |
|||
{ |
|||
"project_info": { |
|||
"project_number": "907754492821", |
|||
"project_id": "deepsound-e0e3d", |
|||
"storage_bucket": "deepsound-e0e3d.firebasestorage.app" |
|||
}, |
|||
"client": [ |
|||
{ |
|||
"client_info": { |
|||
"mobilesdk_app_id": "1:907754492821:android:2deffc5aa1037ae2145c9c", |
|||
"android_client_info": { |
|||
"package_name": "com.yunqiinnovation.deepsound" |
|||
} |
|||
}, |
|||
"oauth_client": [ |
|||
{ |
|||
"client_id": "907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com", |
|||
"client_type": 1, |
|||
"android_info": { |
|||
"package_name": "com.yunqiinnovation.deepsound", |
|||
"certificate_hash": "5a9520045bc16fb4f09fb97668a8b0fc4babd376" |
|||
} |
|||
}, |
|||
{ |
|||
"client_id": "907754492821-15btgolbd7thaa8fpt7s8hs8hq3jl0lg.apps.googleusercontent.com", |
|||
"client_type": 3 |
|||
} |
|||
], |
|||
"api_key": [ |
|||
{ |
|||
"current_key": "AIzaSyAj0_-nS3ygc8VFe1azotSE3-HGZYClkTs" |
|||
} |
|||
], |
|||
"services": { |
|||
"appinvite_service": { |
|||
"other_platform_oauth_client": [ |
|||
{ |
|||
"client_id": "907754492821-15btgolbd7thaa8fpt7s8hs8hq3jl0lg.apps.googleusercontent.com", |
|||
"client_type": 3 |
|||
} |
|||
] |
|||
} |
|||
} |
|||
} |
|||
], |
|||
"configuration_version": "1" |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
storePassword=13071376716 |
|||
keyPassword=13071376716 |
|||
keyAlias=DeepSound |
|||
storeFile=sign/DeepSound.keystore |
|||
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<resources> |
|||
<string name="facebook_app_id">3829022037361855</string> |
|||
<string name="fb_login_protocol_scheme">fb3829022037361855</string> |
|||
<string name="facebook_client_token">193d0a2b1757c41cfa557ea8e8f21c33</string> |
|||
</resources> |
|||
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||
{"flutter":{"platforms":{"android":{"default":{"projectId":"deepsound-e0e3d","appId":"1:907754492821:android:2deffc5aa1037ae2145c9c","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"deepsound-e0e3d","configurations":{"android":"1:907754492821:android:2deffc5aa1037ae2145c9c","ios":"1:907754492821:ios:8c1d7d1b583aec22145c9c","macos":"1:907754492821:ios:8c1d7d1b583aec22145c9c","web":"1:907754492821:web:99451e7efd7d120a145c9c","windows":"1:907754492821:web:e3e8040111db6871145c9c"}}}}}} |
|||
@ -0,0 +1,92 @@ |
|||
// File generated by FlutterFire CLI. |
|||
// ignore_for_file: type=lint |
|||
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; |
|||
import 'package:flutter/foundation.dart' |
|||
show defaultTargetPlatform, kIsWeb, TargetPlatform; |
|||
|
|||
/// Default [FirebaseOptions] for use with your Firebase apps. |
|||
/// |
|||
/// Example: |
|||
/// ```dart |
|||
/// import 'firebase_options.dart'; |
|||
/// // ... |
|||
/// await Firebase.initializeApp( |
|||
/// options: DefaultFirebaseOptions.currentPlatform, |
|||
/// ); |
|||
/// ``` |
|||
class DefaultFirebaseOptions { |
|||
static FirebaseOptions get currentPlatform { |
|||
if (kIsWeb) { |
|||
return web; |
|||
} |
|||
switch (defaultTargetPlatform) { |
|||
case TargetPlatform.android: |
|||
return android; |
|||
case TargetPlatform.iOS: |
|||
return ios; |
|||
case TargetPlatform.macOS: |
|||
return macos; |
|||
case TargetPlatform.windows: |
|||
return windows; |
|||
case TargetPlatform.linux: |
|||
throw UnsupportedError( |
|||
'DefaultFirebaseOptions have not been configured for linux - ' |
|||
'you can reconfigure this by running the FlutterFire CLI again.', |
|||
); |
|||
default: |
|||
throw UnsupportedError( |
|||
'DefaultFirebaseOptions are not supported for this platform.', |
|||
); |
|||
} |
|||
} |
|||
|
|||
static const FirebaseOptions web = FirebaseOptions( |
|||
apiKey: 'AIzaSyBJXQTXsEmY_XA7ZEOusxG6VF5CaKtjHCA', |
|||
appId: '1:907754492821:web:99451e7efd7d120a145c9c', |
|||
messagingSenderId: '907754492821', |
|||
projectId: 'deepsound-e0e3d', |
|||
authDomain: 'deepsound-e0e3d.firebaseapp.com', |
|||
storageBucket: 'deepsound-e0e3d.firebasestorage.app', |
|||
measurementId: 'G-ETX6DF35J3', |
|||
); |
|||
|
|||
static const FirebaseOptions android = FirebaseOptions( |
|||
apiKey: 'AIzaSyAj0_-nS3ygc8VFe1azotSE3-HGZYClkTs', |
|||
appId: '1:907754492821:android:2deffc5aa1037ae2145c9c', |
|||
messagingSenderId: '907754492821', |
|||
projectId: 'deepsound-e0e3d', |
|||
storageBucket: 'deepsound-e0e3d.firebasestorage.app', |
|||
); |
|||
|
|||
static const FirebaseOptions ios = FirebaseOptions( |
|||
apiKey: 'AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808', |
|||
appId: '1:907754492821:ios:8c1d7d1b583aec22145c9c', |
|||
messagingSenderId: '907754492821', |
|||
projectId: 'deepsound-e0e3d', |
|||
storageBucket: 'deepsound-e0e3d.firebasestorage.app', |
|||
androidClientId: '907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com', |
|||
iosClientId: '907754492821-ej6j6pfto66jfhpj47b9bjs9amvo9dde.apps.googleusercontent.com', |
|||
iosBundleId: 'com.example.client', |
|||
); |
|||
|
|||
static const FirebaseOptions macos = FirebaseOptions( |
|||
apiKey: 'AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808', |
|||
appId: '1:907754492821:ios:8c1d7d1b583aec22145c9c', |
|||
messagingSenderId: '907754492821', |
|||
projectId: 'deepsound-e0e3d', |
|||
storageBucket: 'deepsound-e0e3d.firebasestorage.app', |
|||
androidClientId: '907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com', |
|||
iosClientId: '907754492821-ej6j6pfto66jfhpj47b9bjs9amvo9dde.apps.googleusercontent.com', |
|||
iosBundleId: 'com.example.client', |
|||
); |
|||
|
|||
static const FirebaseOptions windows = FirebaseOptions( |
|||
apiKey: 'AIzaSyBJXQTXsEmY_XA7ZEOusxG6VF5CaKtjHCA', |
|||
appId: '1:907754492821:web:e3e8040111db6871145c9c', |
|||
messagingSenderId: '907754492821', |
|||
projectId: 'deepsound-e0e3d', |
|||
authDomain: 'deepsound-e0e3d.firebaseapp.com', |
|||
storageBucket: 'deepsound-e0e3d.firebasestorage.app', |
|||
measurementId: 'G-SKGXEK5TJM', |
|||
); |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
import 'package:firebase_auth/firebase_auth.dart'; |
|||
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; |
|||
import 'package:get/get.dart'; |
|||
|
|||
class Facebook { |
|||
String token = ''; |
|||
// FaceBook登录 |
|||
// 修改后的 signInWithFacebook |
|||
Future<bool> signInWithFacebook() async { |
|||
try { |
|||
print("开始 Facebook 登录"); |
|||
final LoginResult result = await FacebookAuth.instance.login(); |
|||
print("Facebook 登录结果:$result"); |
|||
|
|||
if (result.status == LoginStatus.success) { |
|||
token = result.accessToken!.tokenString; |
|||
print("Facebook Token: $token"); |
|||
|
|||
final credential = FacebookAuthProvider.credential(token); |
|||
print("Facebook Token: $credential"); |
|||
final userData = await FacebookAuth.instance.getUserData(); |
|||
print("user login facebook ${userData..values}"); |
|||
|
|||
return true; |
|||
} else { |
|||
print("登录失败,状态:${result.status},消息:${result.message}"); |
|||
return false; |
|||
} |
|||
} catch (e) { |
|||
print("Facebook 登录异常:$e"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
// 退出Google登录 |
|||
Future<void> signOutFacebook() async { |
|||
await FacebookAuth.instance.logOut(); |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
import 'package:firebase_auth/firebase_auth.dart'; |
|||
import 'package:google_sign_in/google_sign_in.dart'; |
|||
import 'package:get/get.dart'; |
|||
|
|||
class Google { |
|||
String? token = ''; |
|||
String? email = ''; |
|||
String? displayName = ''; |
|||
String? id = ''; |
|||
String? accessToken = ''; |
|||
|
|||
// 登录并返回 |
|||
Future<bool> signInWithGoogle() async { |
|||
try { |
|||
// 确保用户已经登录,并获取当前用户的身份验证令牌 |
|||
final currentUser = FirebaseAuth.instance.currentUser; |
|||
print('Future响应数据: $currentUser'); |
|||
if (currentUser != null) { |
|||
// 这里处理您需要使用这个JWT令牌的逻辑,例如将它存储到本地存储中作为凭据。 |
|||
|
|||
token != await currentUser.getIdToken(); |
|||
|
|||
print('Future响应数据tokenResult: $token'); |
|||
|
|||
return true; |
|||
} else { |
|||
UserCredential userCredential = await loginGoogle(); |
|||
if (token != "") { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
|
|||
// logger.d("user login google: ${userCredential.user?.displayName}"); |
|||
} |
|||
} catch (e) { |
|||
print("Google 登录异常:$e"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
// Google登录 |
|||
Future<UserCredential> loginGoogle() async { |
|||
print('loginGoogle:'); |
|||
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); |
|||
email = googleUser?.email; |
|||
id = googleUser?.id; |
|||
displayName = googleUser?.displayName; |
|||
print('loginGoogle: $googleUser'); |
|||
// Obtain the auth details from the request |
|||
final GoogleSignInAuthentication? googleAuth = |
|||
await googleUser?.authentication; |
|||
accessToken = googleAuth?.accessToken; |
|||
token = googleAuth?.idToken; |
|||
print('loginGoogle:googleAuth?.idToken= ${token}'); |
|||
print('loginGoogle:googleAuth?.accessToken= ${accessToken}'); |
|||
// Create a new credential, |
|||
final credential = GoogleAuthProvider.credential( |
|||
accessToken: googleAuth?.accessToken, |
|||
idToken: googleAuth?.idToken, |
|||
); |
|||
|
|||
// Once signed in, return the UserCredential |
|||
return await FirebaseAuth.instance.signInWithCredential(credential); |
|||
} |
|||
|
|||
// 退出Google登录 |
|||
Future<void> signOutGoogle() async { |
|||
await FirebaseAuth.instance.signOut(); |
|||
await GoogleSignIn().signOut(); |
|||
} |
|||
} |
|||
@ -0,0 +1,889 @@ |
|||
import 'dart:async'; |
|||
import 'dart:convert'; |
|||
import 'dart:io'; |
|||
import 'dart:typed_data'; |
|||
import 'package:fluwx/fluwx.dart'; |
|||
export 'package:fluwx/fluwx.dart'; |
|||
import 'package:http/http.dart' as http; |
|||
import 'package:get/get.dart'; |
|||
|
|||
typedef FlWeChatResponseCallback = void Function(WeChatResponse response); |
|||
|
|||
/// 支持成功 |
|||
typedef FlWeChatPayResponseResultCallback = void Function(); |
|||
|
|||
/// 支付结果回调 |
|||
typedef FlWeChatPayResponseCallback = void Function( |
|||
WeChatPaymentResponse response); |
|||
|
|||
/// LaunchFromWX 回调 |
|||
typedef FlWeChatLaunchFromWXRequestCallback = void Function( |
|||
WeChatLaunchFromWXRequest response); |
|||
|
|||
/// OpenBusinessWebView 回调 |
|||
typedef FlWeChatOpenBusinessWebViewResponseCallback = void Function( |
|||
WeChatOpenBusinessWebviewResponse response); |
|||
|
|||
/// WeChatOpenBusinessView 回调 |
|||
typedef FlWeChatOpenBusinessViewResponseCallback = void Function( |
|||
WeChatOpenBusinessViewResponse response); |
|||
|
|||
/// Share 回调 |
|||
typedef FlWeChatShareResponseCallback = void Function( |
|||
WeChatShareResponse response); |
|||
|
|||
/// Auth 回调 |
|||
typedef FlWeChatAuthResponseCallback = Function(WeChatAuthResponse response); |
|||
|
|||
/// Auth token 回调 |
|||
typedef FlWeChatAuthResponseTokenCallback = Function( |
|||
WeChatAuthResponse response, WXTokenModel token); |
|||
|
|||
/// Auth userinfo 回调 |
|||
typedef FlWeChatAuthResponseUserinfoCallback = Function( |
|||
WeChatAuthResponse response, |
|||
WXTokenModel token, |
|||
WXUserModel userInfo, |
|||
); |
|||
|
|||
/// 显示 toast |
|||
typedef FlWXToastCallback = void Function(String msg); |
|||
|
|||
/// 日志 打印 |
|||
typedef FlWXLogCallback = void Function(String msg); |
|||
|
|||
/// http 请求 |
|||
typedef FlWXHTTPCallback = Future<String?> Function(String url); |
|||
|
|||
class FlWX { |
|||
factory FlWX() => _singleton ??= FlWX._(); |
|||
|
|||
FlWX._(); |
|||
|
|||
static FlWX? _singleton; |
|||
|
|||
final Fluwx fluwx = Fluwx(); |
|||
|
|||
String? _appId; |
|||
|
|||
/// 获取当前设置的 appId |
|||
String? get appId => _appId; |
|||
|
|||
String? _appSecret; |
|||
|
|||
/// 获取当前设置的 appSecret |
|||
String? get appSecret => _appSecret; |
|||
|
|||
String _appName = ''; |
|||
String token = ''; |
|||
String refresh_token = ''; |
|||
String openid = ''; |
|||
// 参数 说明 |
|||
// access_token 接口调用凭证 |
|||
// expires_in access_token 接口调用凭证超时时间,单位(秒) |
|||
// refresh_token 用户刷新 access_token |
|||
// openid 授权用户唯一标识 |
|||
// scope 用户授权的作用域(snsapi_userinfo) |
|||
// unionid 当且仅当该移动应用已获得该用户的 userinfo 授权时,才会出现该字段 |
|||
/// callback params |
|||
FlWXCallbackParams? _params; |
|||
|
|||
/// 分享时的缩略图 |
|||
Uint8List? _shareThumbnail; |
|||
|
|||
/// register |
|||
bool _isRegister = false; |
|||
|
|||
Future<bool> register({ |
|||
required String appId, |
|||
required FlWXCallbackParams params, |
|||
String? universalLink, |
|||
bool doOnIOS = true, |
|||
bool doOnAndroid = true, |
|||
|
|||
/// 用于获取用户信息 |
|||
String? appSecret, |
|||
|
|||
/// app名称(用于分享时使用) |
|||
String appName = '', |
|||
|
|||
/// 分享时的缩略图 |
|||
Uint8List? shareThumbnail, |
|||
}) { |
|||
assert(appId.isNotEmpty); |
|||
if (universalLink != null) assert(universalLink.isNotEmpty); |
|||
if (appSecret != null) { |
|||
assert(appSecret.isNotEmpty); |
|||
} |
|||
_isRegister = true; |
|||
_appId = appId; |
|||
_appSecret = appSecret; |
|||
_appName = appName; |
|||
_shareThumbnail = shareThumbnail; |
|||
_params = params; |
|||
return fluwx.registerApi( |
|||
appId: appId, |
|||
doOnAndroid: doOnAndroid, |
|||
doOnIOS: doOnIOS, |
|||
universalLink: universalLink, |
|||
); |
|||
} |
|||
|
|||
/// 获取微信 用户token |
|||
String _getTokenUrl(String code) { |
|||
print("_getTokenUrl 数据获取${_appId},${_appSecret}"); |
|||
assert( |
|||
_appId != null && |
|||
_appId!.isNotEmpty && |
|||
_appSecret != null && |
|||
_appSecret!.isNotEmpty, |
|||
); |
|||
return 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=$_appId&secret=$_appSecret&code=$code&grant_type=authorization_code'; |
|||
} |
|||
|
|||
/// 获取微信 用户token |
|||
String _getRefreshTokenUrl(String code) { |
|||
print("_getRefreshTokenUrl 数据获取${_appId},${_appSecret}"); |
|||
assert( |
|||
_appId != null && |
|||
_appId!.isNotEmpty && |
|||
_appSecret != null && |
|||
_appSecret!.isNotEmpty, |
|||
); |
|||
return 'https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=$_appId&grant_type=refresh_token&&refresh_token=$refresh_token'; |
|||
} |
|||
|
|||
// GET请求示例 |
|||
Future<Map<String, dynamic>> fetchData(String Url) async { |
|||
try { |
|||
final response = await http.get(Uri.parse(Url)); |
|||
|
|||
if (response.statusCode == 200) { |
|||
final data = jsonDecode(response.body) as Map<String, dynamic>; |
|||
print('GET响应数据: $data'); |
|||
|
|||
return data; |
|||
} else { |
|||
print('GET请求失败,状态码: ${response.statusCode}'); |
|||
return {}; |
|||
} |
|||
} catch (e) { |
|||
print('GET请求异常: $e'); |
|||
return {}; |
|||
} |
|||
} |
|||
|
|||
// POST请求示例 |
|||
Future<bool> postData() async { |
|||
try { |
|||
final response = await http.post( |
|||
Uri.parse('https://jsonplaceholder.typicode.com/posts'), |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, // 设置请求头:ml-citation{ref="6" data="citationList"} |
|||
body: jsonEncode({ |
|||
// 编码JSON数据:ml-citation{ref="3" data="citationList"} |
|||
'title': 'foo', |
|||
'body': 'bar', |
|||
'userId': 1, |
|||
}), |
|||
); |
|||
|
|||
if (response.statusCode == 201) { |
|||
final data = jsonDecode(response.body); |
|||
print('POST响应数据: $data'); |
|||
return true; |
|||
} else { |
|||
print('POST请求失败,状态码: ${response.statusCode}'); |
|||
return false; |
|||
} |
|||
} catch (e) { |
|||
print('POST请求异常: $e'); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// 获取微信 用户信息 |
|||
String _getUserInfoUrl({required String openId, required String token}) => |
|||
'https://api.weixin.qq.com/sns/userinfo?access_token=$token&openid=$openId'; |
|||
//检验授权凭证(access_token)是否有效 |
|||
String _getUserTokenEffertive({ |
|||
required String openId, |
|||
required String token, |
|||
}) => |
|||
'https://api.weixin.qq.com/sns/auth?access_token=$token&openid=$openId'; |
|||
|
|||
/// 是否安装微信 |
|||
Future<bool> get isInstalled async { |
|||
assert(_isRegister, '请先调用 FlWX().register()'); |
|||
final installed = await fluwx.isWeChatInstalled; |
|||
if (!installed) _params!.onToast?.call('该服务需要先安装微信'); |
|||
return installed; |
|||
} |
|||
|
|||
/// isSupportOpenBusinessView |
|||
Future<bool> get isSupportOpenBusinessView async { |
|||
if (!await isInstalled) return false; |
|||
return await fluwx.isSupportOpenBusinessView; |
|||
} |
|||
|
|||
/// 添加移除监听 |
|||
FluwxCancelable _onListener(FlWeChatResponseCallback onResponse) { |
|||
late FluwxCancelable cancelable; |
|||
subscriber(response) { |
|||
onResponse(response); |
|||
Future.delayed(const Duration(milliseconds: 500), () { |
|||
cancelable.cancel(); |
|||
}); |
|||
} |
|||
|
|||
return cancelable = fluwx.addSubscriber(subscriber); |
|||
} |
|||
|
|||
Future<String?> getExtMsg() async { |
|||
if (!await isInstalled) return null; |
|||
return await fluwx.getExtMsg(); |
|||
} |
|||
|
|||
Future<void> attemptToResumeMsgFromWx({ |
|||
Function(WeChatShowMessageFromWXRequest response)? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return; |
|||
await fluwx.attemptToResumeMsgFromWx(); |
|||
if (onResponse != null) { |
|||
_onListener((response) { |
|||
if (response is WeChatShowMessageFromWXRequest) onResponse(response); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
/// 调用微信登录 |
|||
Future<bool> authBy( |
|||
AuthType which, { |
|||
/// 原始数据回调 |
|||
FlWeChatAuthResponseCallback? onResponse, |
|||
|
|||
/// 获取token回调 |
|||
FlWeChatAuthResponseTokenCallback? onToken, |
|||
|
|||
/// 获取userinfo回调 |
|||
FlWeChatAuthResponseUserinfoCallback? onUserinfo, |
|||
bool getUserInfo = false, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
final cancelable = _onListener((response) async { |
|||
if (response is WeChatAuthResponse) { |
|||
onResponse?.call(response); |
|||
print( |
|||
"用户点击事件${response.isSuccessful},${response.code},${response.lang},${response.state},${response.country}", |
|||
); |
|||
if (response.isSuccessful && response.code != null) { |
|||
print("数据获取失败${onToken},${onUserinfo}"); |
|||
// if (onToken != null || onUserinfo != null) { |
|||
try { |
|||
var Istoken = await fetchData( |
|||
_getTokenUrl(response.code!), |
|||
); // 触发GET请求 |
|||
//token有效期是30天,30天后需要重新授权,需要看看API文档 |
|||
print("数据获取fetchData${Istoken}"); |
|||
|
|||
if (Istoken != {}) { |
|||
token = Istoken["access_token"].toString(); |
|||
refresh_token = Istoken["refresh_token"].toString(); |
|||
openid = Istoken["openid"].toString(); |
|||
print("数据获取token=${token}"); |
|||
print("数据获取openid=${openid}"); |
|||
print("数据获取refresh_token=${refresh_token}"); |
|||
Istoken = await fetchData(_getRefreshTokenUrl(response.code!)); |
|||
print("数据获取_getUserTokenEffertive${Istoken}"); |
|||
Istoken = await fetchData( |
|||
_getUserTokenEffertive(openId: openid, token: token), |
|||
); // 触发GET请求 |
|||
print("数据获取_getUserTokenEffertive${Istoken}"); |
|||
Istoken = await fetchData( |
|||
_getUserInfoUrl(openId: openid, token: token), |
|||
); // 触发GET请求 |
|||
print("数据获取_getUserInfoUrl${Istoken}"); |
|||
} |
|||
} catch (e) { |
|||
_params!.onLog?.call('数据解析失败 $e'); |
|||
} |
|||
// } |
|||
} |
|||
} else if (response is WeChatAuthByQRCodeFinishedResponse) {} |
|||
}); |
|||
final result = await fluwx.authBy(which: which); |
|||
print("authBy数据获取${result}"); |
|||
if (!result) { |
|||
cancelable.cancel(); |
|||
_params!.onToast?.call('微信授权失败'); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
Future<bool> autoDeduct( |
|||
AutoDeduct data, { |
|||
FlWeChatResponseCallback? onResponse, |
|||
}) async { |
|||
final cancelable = _onListener((response) { |
|||
onResponse?.call(response); |
|||
}); |
|||
final result = await fluwx.autoDeduct(data: data); |
|||
if (!result) { |
|||
cancelable.cancel(); |
|||
_params!.onToast?.call('微信签约失败'); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/// 调用微信支付 |
|||
Future<bool> pay( |
|||
PayType which, { |
|||
FlWeChatPayResponseResultCallback? onSuccess, |
|||
FlWeChatPayResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
final cancelable = _onListener((response) { |
|||
if (response is WeChatPaymentResponse) { |
|||
onResponse?.call(response); |
|||
_params!.onLog?.call('微信支付 result code: ${response.errCode} '); |
|||
if (response.errCode == 0) { |
|||
_params!.onToast?.call('支付成功'); |
|||
onSuccess?.call(); |
|||
} else if (response.errCode == -2) { |
|||
_params!.onToast?.call('已取消支付'); |
|||
} else { |
|||
_params!.onToast?.call('支付失败'); |
|||
} |
|||
} |
|||
}); |
|||
final result = await fluwx.pay(which: which); |
|||
if (!result) { |
|||
cancelable.cancel(); |
|||
_params!.onToast?.call('支付失败'); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/// ****************** open ****************** /// |
|||
|
|||
Future<bool> open(OpenType target) async { |
|||
if (!await isInstalled) return false; |
|||
return fluwx.open(target: target); |
|||
} |
|||
|
|||
Future<bool> openWeChatApp({ |
|||
FlWeChatLaunchFromWXRequestCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
final cancelable = _onListener((response) { |
|||
if (response is WeChatLaunchFromWXRequest) onResponse?.call(response); |
|||
}); |
|||
final result = await fluwx.open(target: WeChatApp()); |
|||
if (!result) cancelable.cancel(); |
|||
return result; |
|||
} |
|||
|
|||
Future<bool> openBrowser( |
|||
String url, { |
|||
FlWeChatOpenBusinessWebViewResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
final cancelable = _onListener((response) { |
|||
if (response is WeChatOpenBusinessWebviewResponse) { |
|||
onResponse?.call(response); |
|||
} |
|||
}); |
|||
final result = await fluwx.open(target: Browser(url)); |
|||
if (!result) cancelable.cancel(); |
|||
return result; |
|||
} |
|||
|
|||
Future<bool> openRankList() async { |
|||
if (!await isInstalled) return false; |
|||
return fluwx.open(target: RankList()); |
|||
} |
|||
|
|||
Future<bool> openBusinessView({ |
|||
required String businessType, |
|||
required String query, |
|||
FlWeChatOpenBusinessViewResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
final cancelable = _onListener((response) { |
|||
if (response is WeChatOpenBusinessViewResponse) { |
|||
onResponse?.call(response); |
|||
} |
|||
}); |
|||
final result = await fluwx.open( |
|||
target: BusinessView(businessType: businessType, query: query), |
|||
); |
|||
if (!result) cancelable.cancel(); |
|||
return result; |
|||
} |
|||
|
|||
Future<bool> openInvoice({ |
|||
required String appId, |
|||
required String cardType, |
|||
String locationId = '', |
|||
String cardId = '', |
|||
String canMultiSelect = '', |
|||
Function(WeChatOpenInvoiceResponse response)? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
final cancelable = _onListener((response) { |
|||
if (response is WeChatOpenInvoiceResponse) onResponse?.call(response); |
|||
}); |
|||
final result = await fluwx.open( |
|||
target: Invoice( |
|||
appId: appId, |
|||
cardType: cardType, |
|||
locationId: locationId, |
|||
cardId: cardId, |
|||
canMultiSelect: canMultiSelect, |
|||
), |
|||
); |
|||
if (!result) cancelable.cancel(); |
|||
return result; |
|||
} |
|||
|
|||
Future<bool> openCustomerServiceChat({ |
|||
required String corpId, |
|||
required String url, |
|||
Function(WeChatOpenCustomerServiceChatResponse response)? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
final cancelable = _onListener((response) { |
|||
if (response is WeChatOpenCustomerServiceChatResponse) { |
|||
onResponse?.call(response); |
|||
} |
|||
}); |
|||
final result = await fluwx.open( |
|||
target: CustomerServiceChat(corpId: corpId, url: url), |
|||
); |
|||
if (!result) cancelable.cancel(); |
|||
return result; |
|||
} |
|||
|
|||
Future<bool> openMiniProgram( |
|||
String username, { |
|||
String? path, |
|||
Function(WeChatLaunchMiniProgramResponse response)? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
final cancelable = _onListener((response) { |
|||
if (response is WeChatLaunchMiniProgramResponse) { |
|||
onResponse?.call(response); |
|||
} |
|||
}); |
|||
final result = await fluwx.open( |
|||
target: MiniProgram(username: username, path: path), |
|||
); |
|||
if (!result) { |
|||
cancelable.cancel(); |
|||
_params?.onToast?.call('小程序打开失败'); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
Future<bool> openSubscribeMessage({ |
|||
required String appId, |
|||
required int scene, |
|||
required String templateId, |
|||
String? reserved, |
|||
Function(WeChatSubscribeMsgResponse response)? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
final cancelable = _onListener((response) { |
|||
if (response is WeChatSubscribeMsgResponse) onResponse?.call(response); |
|||
}); |
|||
final result = await fluwx.open( |
|||
target: SubscribeMessage( |
|||
appId: appId, |
|||
scene: scene, |
|||
templateId: templateId, |
|||
reserved: reserved, |
|||
), |
|||
); |
|||
if (!result) cancelable.cancel(); |
|||
return result; |
|||
} |
|||
|
|||
/// ****************** share ****************** /// |
|||
|
|||
Future<bool> share( |
|||
WeChatShareModel what, { |
|||
FlWeChatShareResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
return _shareResponse(fluwx.share(what), onResponse: onResponse); |
|||
} |
|||
|
|||
Future<bool> shareText( |
|||
String source, { |
|||
String? title, |
|||
String? description, |
|||
WeChatScene scene = WeChatScene.session, |
|||
String? messageExt, |
|||
String? messageAction, |
|||
String? mediaTagName, |
|||
String? msgSignature, |
|||
Uint8List? thumbData, |
|||
String? thumbDataHash, |
|||
FlWeChatShareResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
return _shareResponse( |
|||
fluwx.share( |
|||
WeChatShareTextModel( |
|||
source, |
|||
title: title ?? _appName, |
|||
description: description ?? _appName, |
|||
scene: scene, |
|||
messageExt: messageExt, |
|||
messageAction: messageAction, |
|||
mediaTagName: mediaTagName, |
|||
msgSignature: msgSignature, |
|||
thumbData: thumbData ?? _shareThumbnail, |
|||
thumbDataHash: thumbDataHash, |
|||
), |
|||
), |
|||
onResponse: onResponse, |
|||
); |
|||
} |
|||
|
|||
Future<bool> shareMiniProgram( |
|||
String webPage, { |
|||
required String webPageUrl, |
|||
WXMiniProgramType miniProgramType = WXMiniProgramType.release, |
|||
required String userName, |
|||
String path = "/", |
|||
String? title, |
|||
String? description, |
|||
bool withShareTicket = false, |
|||
String? messageExt, |
|||
String? messageAction, |
|||
String? mediaTagName, |
|||
String? msgSignature, |
|||
Uint8List? thumbData, |
|||
String? thumbDataHash, |
|||
FlWeChatShareResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
return _shareResponse( |
|||
fluwx.share( |
|||
WeChatShareMiniProgramModel( |
|||
webPageUrl: webPageUrl, |
|||
userName: userName, |
|||
title: title ?? _appName, |
|||
description: description ?? _appName, |
|||
messageExt: messageExt, |
|||
messageAction: messageAction, |
|||
mediaTagName: mediaTagName, |
|||
msgSignature: msgSignature, |
|||
thumbData: thumbData ?? _shareThumbnail, |
|||
thumbDataHash: thumbDataHash, |
|||
), |
|||
), |
|||
onResponse: onResponse, |
|||
); |
|||
} |
|||
|
|||
Future<bool> shareImage( |
|||
WeChatImageToShare source, { |
|||
String? title, |
|||
String? description, |
|||
WeChatScene scene = WeChatScene.session, |
|||
String? messageExt, |
|||
String? messageAction, |
|||
String? mediaTagName, |
|||
String? msgSignature, |
|||
Uint8List? thumbData, |
|||
String? thumbDataHash, |
|||
FlWeChatShareResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
return _shareResponse( |
|||
fluwx.share( |
|||
WeChatShareImageModel( |
|||
source, |
|||
title: title ?? _appName, |
|||
description: description ?? _appName, |
|||
scene: scene, |
|||
messageExt: messageExt, |
|||
messageAction: messageAction, |
|||
mediaTagName: mediaTagName, |
|||
msgSignature: msgSignature, |
|||
thumbData: thumbData ?? _shareThumbnail, |
|||
thumbDataHash: thumbDataHash, |
|||
), |
|||
), |
|||
onResponse: onResponse, |
|||
); |
|||
} |
|||
|
|||
Future<bool> shareMusic({ |
|||
String? musicUrl, |
|||
String? musicDataUrl, |
|||
String? musicLowBandUrl, |
|||
String? musicLowBandDataUrl, |
|||
String? title, |
|||
String? description, |
|||
WeChatScene scene = WeChatScene.session, |
|||
String? messageExt, |
|||
String? messageAction, |
|||
String? mediaTagName, |
|||
String? msgSignature, |
|||
Uint8List? thumbData, |
|||
String? thumbDataHash, |
|||
FlWeChatShareResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
return _shareResponse( |
|||
fluwx.share( |
|||
WeChatShareMusicModel( |
|||
musicUrl: musicUrl, |
|||
musicDataUrl: musicDataUrl, |
|||
musicLowBandUrl: musicLowBandUrl, |
|||
musicLowBandDataUrl: musicLowBandDataUrl, |
|||
title: title ?? _appName, |
|||
description: description ?? _appName, |
|||
scene: scene, |
|||
messageExt: messageExt, |
|||
messageAction: messageAction, |
|||
mediaTagName: mediaTagName, |
|||
msgSignature: msgSignature, |
|||
thumbData: thumbData ?? _shareThumbnail, |
|||
thumbDataHash: thumbDataHash, |
|||
), |
|||
), |
|||
onResponse: onResponse, |
|||
); |
|||
} |
|||
|
|||
Future<bool> shareVideo( |
|||
String videoUrl, { |
|||
String? title, |
|||
String? description, |
|||
WeChatScene scene = WeChatScene.session, |
|||
String? messageExt, |
|||
String? messageAction, |
|||
String? mediaTagName, |
|||
String? msgSignature, |
|||
Uint8List? thumbData, |
|||
String? thumbDataHash, |
|||
FlWeChatShareResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
return _shareResponse( |
|||
fluwx.share( |
|||
WeChatShareVideoModel( |
|||
title: title ?? _appName, |
|||
videoUrl: videoUrl, |
|||
description: description ?? _appName, |
|||
scene: scene, |
|||
messageExt: messageExt, |
|||
messageAction: messageAction, |
|||
mediaTagName: mediaTagName, |
|||
msgSignature: msgSignature, |
|||
thumbData: thumbData ?? _shareThumbnail, |
|||
thumbDataHash: thumbDataHash, |
|||
), |
|||
), |
|||
onResponse: onResponse, |
|||
); |
|||
} |
|||
|
|||
Future<bool> shareWebPage( |
|||
String webPage, { |
|||
String? title, |
|||
String? description, |
|||
WeChatScene scene = WeChatScene.session, |
|||
String? messageExt, |
|||
String? messageAction, |
|||
String? mediaTagName, |
|||
String? msgSignature, |
|||
Uint8List? thumbData, |
|||
String? thumbDataHash, |
|||
FlWeChatShareResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
return _shareResponse( |
|||
fluwx.share( |
|||
WeChatShareWebPageModel( |
|||
webPage, |
|||
title: title ?? _appName, |
|||
description: description ?? _appName, |
|||
scene: scene, |
|||
messageExt: messageExt, |
|||
messageAction: messageAction, |
|||
mediaTagName: mediaTagName, |
|||
msgSignature: msgSignature, |
|||
thumbData: thumbData ?? _shareThumbnail, |
|||
thumbDataHash: thumbDataHash, |
|||
), |
|||
), |
|||
onResponse: onResponse, |
|||
); |
|||
} |
|||
|
|||
Future<bool> shareFile( |
|||
File source, { |
|||
required String suffix, |
|||
String? title, |
|||
String? description, |
|||
WeChatScene scene = WeChatScene.session, |
|||
String? messageExt, |
|||
String? messageAction, |
|||
String? mediaTagName, |
|||
String? msgSignature, |
|||
Uint8List? thumbData, |
|||
String? thumbDataHash, |
|||
FlWeChatShareResponseCallback? onResponse, |
|||
}) async { |
|||
if (!await isInstalled) return false; |
|||
return _shareResponse( |
|||
fluwx.share( |
|||
WeChatShareFileModel( |
|||
WeChatFile.file(source, suffix: suffix), |
|||
title: title ?? _appName, |
|||
description: description ?? _appName, |
|||
scene: scene, |
|||
messageExt: messageExt, |
|||
messageAction: messageAction, |
|||
mediaTagName: mediaTagName, |
|||
msgSignature: msgSignature, |
|||
thumbData: thumbData ?? _shareThumbnail, |
|||
thumbDataHash: thumbDataHash, |
|||
), |
|||
), |
|||
onResponse: onResponse, |
|||
); |
|||
} |
|||
|
|||
Future<bool> _shareResponse( |
|||
Future<bool> share, { |
|||
FlWeChatShareResponseCallback? onResponse, |
|||
}) async { |
|||
final cancelable = _onListener((response) { |
|||
if (response is WeChatShareResponse) onResponse?.call(response); |
|||
}); |
|||
final result = await share; |
|||
if (!result) cancelable.cancel(); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
class FlWXCallbackParams { |
|||
FlWXCallbackParams({this.onToast, this.onLog, this.onHttp}); |
|||
|
|||
/// toast 显示 |
|||
final FlWXToastCallback? onToast; |
|||
|
|||
/// 日志 打印 |
|||
final FlWXLogCallback? onLog; |
|||
|
|||
/// http 请求 返回 json |
|||
final FlWXHTTPCallback? onHttp; |
|||
} |
|||
|
|||
class WXUserModel { |
|||
final String? openid; |
|||
final String? nickname; |
|||
final int? sex; |
|||
final String? language; |
|||
final String? city; |
|||
final String? province; |
|||
final String? country; |
|||
final String? headImgUrl; |
|||
final List<dynamic>? privilege; |
|||
final String? unionId; |
|||
|
|||
WXUserModel.fromJson(Map<String, dynamic> json) |
|||
: openid = json['openid'] as String?, |
|||
nickname = json['nickname'] as String?, |
|||
sex = json['sex'] as int?, |
|||
language = json['language'] as String?, |
|||
city = json['city'] as String?, |
|||
province = json['province'] as String?, |
|||
country = json['country'] as String?, |
|||
headImgUrl = json['headimgurl'] as String?, |
|||
privilege = json['privilege'] as List<dynamic>?, |
|||
unionId = json['unionid'] as String?; |
|||
|
|||
Map<String, dynamic> toMap() => { |
|||
'openid': openid, |
|||
'unionId': unionId, |
|||
'nickname': nickname, |
|||
'sex': sex, |
|||
'headImgUrl': headImgUrl, |
|||
'privilege': privilege, |
|||
'language': language, |
|||
'city': city, |
|||
'province': province, |
|||
'country': country, |
|||
}; |
|||
} |
|||
|
|||
class WXTokenModel { |
|||
final String? accessToken; |
|||
final int? expiresIn; |
|||
final String? refreshToken; |
|||
final String? openid; |
|||
final String? scope; |
|||
final String? unionId; |
|||
|
|||
WXTokenModel.fromJson(Map<String, dynamic> json) |
|||
: accessToken = json['access_token'] as String?, |
|||
expiresIn = json['expires_in'] as int?, |
|||
refreshToken = json['refresh_token'] as String?, |
|||
openid = json['openid'] as String?, |
|||
scope = json['scope'] as String?, |
|||
unionId = json['unionid'] as String?; |
|||
|
|||
Map<String, dynamic> toMap() => { |
|||
'openid': openid, |
|||
'unionId': unionId, |
|||
'accessToken': accessToken, |
|||
'expiresIn': expiresIn, |
|||
'refreshToken': refreshToken, |
|||
'scope': scope, |
|||
}; |
|||
} |
|||
|
|||
extension ExtensionMap on Map { |
|||
Payment? toPayment() { |
|||
try { |
|||
final appId = (this['appId'] ?? this['appid']); |
|||
final partnerId = (this['partnerId'] ?? |
|||
this['partnerid'] ?? |
|||
this['partner']) as String?; |
|||
final prepayId = (this['prepayId'] ?? this['prepayid'] ?? this['prepay']); |
|||
final packageValue = |
|||
(this['packageValue'] ?? this['packagevalue'] ?? this['package']); |
|||
final nonceStr = (this['nonceStr'] ?? this['noncestr'] ?? this['nonce']); |
|||
final timestamp = (this['timestamp'] ?? this['timeStamp']); |
|||
final sign = this['sign']; |
|||
if (appId != null && |
|||
partnerId != null && |
|||
prepayId != null && |
|||
packageValue != null && |
|||
nonceStr != null && |
|||
timestamp != null && |
|||
sign != null) { |
|||
return Payment( |
|||
appId: appId.toString(), |
|||
partnerId: partnerId.toString(), |
|||
prepayId: prepayId.toString(), |
|||
packageValue: packageValue.toString(), |
|||
nonceStr: nonceStr.toString(), |
|||
timestamp: int.parse(timestamp!.toString()), |
|||
sign: sign.toString(), |
|||
); |
|||
} |
|||
} catch (e) { |
|||
FlWX()._params?.onLog?.call(e.toString()); |
|||
} |
|||
return null; |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../controllers/meeting_controller.dart'; |
|||
|
|||
class MeetingBinding extends Bindings { |
|||
@override |
|||
void dependencies() { |
|||
// 注册控制器 |
|||
Get.lazyPut<MeetingController>(() => MeetingController()); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
|
|||
class MeetingController extends GetxController |
|||
with GetSingleTickerProviderStateMixin { |
|||
late AnimationController lottieController; |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
lottieController = AnimationController(vsync: this); |
|||
} |
|||
|
|||
@override |
|||
void onClose() { |
|||
lottieController.dispose(); |
|||
super.onClose(); |
|||
} |
|||
} |
|||
@ -0,0 +1,227 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:lottie/lottie.dart'; |
|||
|
|||
import '../../../core/theme/app_colors.dart'; |
|||
import '../controllers/meeting_controller.dart'; |
|||
|
|||
class MeetingView extends GetView<MeetingController> { |
|||
const MeetingView({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
final isDarkMode = Theme.of(context).brightness == Brightness.dark; |
|||
|
|||
return Scaffold( |
|||
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], |
|||
appBar: _buildAppBar(isDarkMode), |
|||
body: _buildBody(isDarkMode), |
|||
); |
|||
} |
|||
|
|||
// 构建应用栏 |
|||
PreferredSizeWidget _buildAppBar(bool isDarkMode) { |
|||
return AppBar( |
|||
backgroundColor: isDarkMode ? Colors.black : Colors.white, |
|||
elevation: 0.5, |
|||
leading: IconButton( |
|||
icon: Icon( |
|||
Icons.arrow_back_ios, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
size: 20.sp, |
|||
), |
|||
onPressed: () => Get.back(), |
|||
), |
|||
title: Padding( |
|||
padding: const EdgeInsets.only(right: 56), |
|||
child: Row( |
|||
mainAxisAlignment: MainAxisAlignment.center, |
|||
children: [ |
|||
Icon( |
|||
Icons.keyboard_voice, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
), |
|||
5.horizontalSpace, |
|||
Text( |
|||
'00:00:00', |
|||
style: TextStyle( |
|||
fontSize: 17.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
centerTitle: true, |
|||
); |
|||
} |
|||
|
|||
// 构建主体内容 |
|||
Widget _buildBody(bool isDarkMode) { |
|||
return Padding( |
|||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.w), |
|||
child: Column( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
children: [ |
|||
Row( |
|||
children: [ |
|||
Expanded( |
|||
child: Text( |
|||
'2025-03-03 录音', |
|||
style: TextStyle( |
|||
color: isDarkMode ? Colors.white : Colors.black87, |
|||
fontSize: 17.sp, |
|||
), |
|||
), |
|||
), |
|||
10.horizontalSpace, |
|||
Text( |
|||
'中文', |
|||
style: TextStyle( |
|||
color: isDarkMode ? Colors.white : Colors.black87, |
|||
fontSize: 15.sp, |
|||
), |
|||
), |
|||
], |
|||
), |
|||
Padding( |
|||
padding: EdgeInsets.symmetric(vertical: 5.w), |
|||
child: Text( |
|||
'2025-03-03 16:18', |
|||
style: TextStyle( |
|||
color: isDarkMode ? Colors.grey[400] : Colors.grey[500], |
|||
fontSize: 14.sp, |
|||
), |
|||
), |
|||
), |
|||
_audioWave(isDarkMode), |
|||
_recordContent(isDarkMode), |
|||
_bottomButton(isDarkMode), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
// 音波 |
|||
Widget _audioWave(bool isDarkMode) { |
|||
return Container( |
|||
width: double.infinity, |
|||
height: 70.w, |
|||
margin: EdgeInsets.symmetric(vertical: 5.w), |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, |
|||
borderRadius: BorderRadius.circular(12.w), |
|||
boxShadow: [ |
|||
BoxShadow( |
|||
color: isDarkMode |
|||
? Colors.black.withOpacity(0.2) |
|||
: Colors.black.withOpacity(0.05), |
|||
blurRadius: 10, |
|||
offset: const Offset(0, 2), |
|||
), |
|||
], |
|||
), |
|||
child: Row( |
|||
children: [ |
|||
Expanded( |
|||
child: Lottie.asset( |
|||
'assets/lottie/audio_wave.json', |
|||
controller: controller.lottieController, |
|||
fit: BoxFit.fill, |
|||
onLoaded: (composition) { |
|||
controller.lottieController.duration = composition.duration; |
|||
}, |
|||
), |
|||
), |
|||
Expanded( |
|||
child: Lottie.asset( |
|||
'assets/lottie/audio_wave.json', |
|||
controller: controller.lottieController, |
|||
fit: BoxFit.fill, |
|||
), |
|||
), |
|||
Expanded( |
|||
child: Lottie.asset( |
|||
'assets/lottie/audio_wave.json', |
|||
controller: controller.lottieController, |
|||
fit: BoxFit.fill, |
|||
), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
// 记录内容 |
|||
Widget _recordContent(bool isDarkMode) { |
|||
return Expanded( |
|||
child: Container( |
|||
width: double.infinity, |
|||
padding: EdgeInsets.all(10.w), |
|||
margin: EdgeInsets.symmetric(vertical: 5.w), |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, |
|||
borderRadius: BorderRadius.circular(12.w), |
|||
boxShadow: [ |
|||
BoxShadow( |
|||
color: isDarkMode |
|||
? Colors.black.withOpacity(0.2) |
|||
: Colors.black.withOpacity(0.05), |
|||
blurRadius: 10, |
|||
offset: const Offset(0, 2), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
); |
|||
} |
|||
|
|||
// 底部按钮 |
|||
Widget _bottomButton(bool isDarkMode) { |
|||
return Padding( |
|||
padding: EdgeInsets.only(top: 40.w, bottom: 30.w), |
|||
child: Row( |
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|||
children: [ |
|||
GestureDetector( |
|||
onTap: () {}, |
|||
child: Container( |
|||
width: 130.w, |
|||
height: 60.w, |
|||
decoration: BoxDecoration( |
|||
borderRadius: BorderRadius.circular(60.w), |
|||
border: Border.all( |
|||
color: isDarkMode ? AppColors.primary : Colors.blue, |
|||
width: 2.w, |
|||
), |
|||
), |
|||
child: Icon( |
|||
Icons.pause, |
|||
color: isDarkMode ? AppColors.primary : Colors.blue, |
|||
size: 40.w, |
|||
), |
|||
), |
|||
), |
|||
GestureDetector( |
|||
onTap: () {}, |
|||
child: Container( |
|||
width: 130.w, |
|||
height: 60.w, |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode ? AppColors.primary : Colors.blue, |
|||
borderRadius: BorderRadius.circular(60.w), |
|||
), |
|||
child: Icon( |
|||
Icons.fiber_manual_record_outlined, |
|||
color: Colors.white, |
|||
size: 40.w, |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue