10 changed files with 1012 additions and 65 deletions
@ -0,0 +1,158 @@ |
|||
import 'dart:convert'; |
|||
import 'dart:io'; |
|||
import 'dart:math'; |
|||
import 'package:firebase_auth/firebase_auth.dart'; |
|||
import 'package:flutter/foundation.dart'; |
|||
import 'package:sign_in_with_apple/sign_in_with_apple.dart'; |
|||
import 'package:crypto/crypto.dart'; |
|||
import 'package:get/get.dart'; |
|||
|
|||
class Apple { |
|||
String? token = ''; |
|||
String name = ''; |
|||
String email = ''; |
|||
String pictureurl = ''; |
|||
String id = ''; |
|||
|
|||
/// 生成随机nonce字符串 |
|||
String generateNonce([int length = 32]) { |
|||
const charset = |
|||
'0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._'; |
|||
final random = Random.secure(); |
|||
return List.generate(length, (_) => charset[random.nextInt(charset.length)]) |
|||
.join(); |
|||
} |
|||
|
|||
/// 返回输入字符串的sha256哈希值(十六进制表示) |
|||
String sha256ofString(String input) { |
|||
final bytes = utf8.encode(input); |
|||
final digest = sha256.convert(bytes); |
|||
return digest.toString(); |
|||
} |
|||
|
|||
/// Apple登录主方法 |
|||
Future<bool> signInWithApple() async { |
|||
try { |
|||
// 检查Apple登录可用性 |
|||
if (!await SignInWithApple.isAvailable()) { |
|||
if (kDebugMode) { |
|||
print('Apple登录不可用'); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
UserCredential userCredential = await loginApple(); |
|||
token = await userCredential.user?.getIdToken(); |
|||
|
|||
if (kDebugMode) { |
|||
print('Apple登录响应数据token: $token'); |
|||
} |
|||
|
|||
return token != null && token!.isNotEmpty; |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
print('Apple登录异常:$e'); |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// 执行Apple登录流程 |
|||
Future<UserCredential> loginApple() async { |
|||
// 生成nonce用于安全验证 |
|||
final rawNonce = generateNonce(); |
|||
final nonce = sha256ofString(rawNonce); |
|||
|
|||
try { |
|||
// 请求Apple登录凭证 |
|||
final appleCredential = await SignInWithApple.getAppleIDCredential( |
|||
scopes: [ |
|||
AppleIDAuthorizationScopes.email, |
|||
AppleIDAuthorizationScopes.fullName, |
|||
], |
|||
nonce: nonce, |
|||
); |
|||
|
|||
// 提取用户信息 |
|||
_extractUserInfo(appleCredential); |
|||
|
|||
// 创建Firebase OAuth凭证 |
|||
final oauthCredential = OAuthProvider("apple.com").credential( |
|||
idToken: appleCredential.identityToken, |
|||
rawNonce: rawNonce, |
|||
); |
|||
|
|||
if (kDebugMode) { |
|||
print('Apple OAuth凭证创建成功'); |
|||
print('用户信息: name=$name, email=$email, id=$id'); |
|||
} |
|||
|
|||
// 使用Firebase Auth进行登录 |
|||
return await FirebaseAuth.instance.signInWithCredential(oauthCredential); |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
print('Apple登录流程异常:$e'); |
|||
} |
|||
throw Exception('Apple登录失败: $e'); |
|||
} |
|||
} |
|||
|
|||
/// 提取Apple返回的用户信息 |
|||
void _extractUserInfo(AuthorizationCredentialAppleID credential) { |
|||
// 用户ID |
|||
id = credential.userIdentifier ?? ''; |
|||
|
|||
// 邮箱(可能为空,如果用户选择隐藏邮箱) |
|||
email = credential.email ?? ''; |
|||
|
|||
// 用户姓名(仅首次登录时提供) |
|||
if (credential.givenName != null || credential.familyName != null) { |
|||
final givenName = credential.givenName ?? ''; |
|||
final familyName = credential.familyName ?? ''; |
|||
name = '$givenName $familyName'.trim(); |
|||
} |
|||
|
|||
// Apple不提供头像,使用默认值 |
|||
pictureurl = ''; |
|||
|
|||
if (kDebugMode) { |
|||
print('提取的用户信息:'); |
|||
print('ID: $id'); |
|||
print('Email: $email'); |
|||
print('Name: $name'); |
|||
print('Picture URL: $pictureurl'); |
|||
} |
|||
} |
|||
|
|||
/// 退出Apple登录 |
|||
Future<void> signOutApple() async { |
|||
try { |
|||
await FirebaseAuth.instance.signOut(); |
|||
// 清空用户信息 |
|||
token = ''; |
|||
name = ''; |
|||
email = ''; |
|||
pictureurl = ''; |
|||
id = ''; |
|||
|
|||
if (kDebugMode) { |
|||
print('Apple登录退出成功'); |
|||
} |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
print('Apple登录退出异常:$e'); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// 检查Apple登录环境 |
|||
static Future<void> checkAppleSignInEnvironment() async { |
|||
if (kDebugMode) { |
|||
print('=== Apple登录环境检查 ==='); |
|||
print('平台: ${GetPlatform.isIOS ? "iOS" : "非iOS"}'); |
|||
print('Apple登录可用性: ${await SignInWithApple.isAvailable()}'); |
|||
print('Firebase Auth实例: ${FirebaseAuth.instance.currentUser?.uid ?? "未登录"}'); |
|||
print('========================'); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,169 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|||
import 'package:get/get.dart'; |
|||
|
|||
import '../../../common/webview_page.dart'; |
|||
|
|||
class MemberBottomSheet extends StatelessWidget { |
|||
const MemberBottomSheet({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Container( |
|||
padding: EdgeInsets.symmetric(horizontal: 12.w), |
|||
decoration: BoxDecoration( |
|||
borderRadius: BorderRadius.vertical( |
|||
top: Radius.circular(12.r), |
|||
), |
|||
image: const DecorationImage( |
|||
alignment: Alignment.topCenter, |
|||
image: AssetImage('assets/images/mine-background.png'), |
|||
fit: BoxFit.fitWidth, |
|||
), |
|||
), |
|||
child: Column( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
mainAxisSize: MainAxisSize.min, |
|||
children: [ |
|||
Container( |
|||
margin: EdgeInsets.only(top: 12.w), |
|||
alignment: Alignment.centerRight, |
|||
child: GestureDetector( |
|||
onTap: () => Get.back(), |
|||
child: Container( |
|||
width: 26.w, |
|||
height: 26.w, |
|||
decoration: BoxDecoration( |
|||
color: Colors.white, |
|||
borderRadius: BorderRadius.circular(26.r), |
|||
), |
|||
child: Icon( |
|||
Icons.close, |
|||
size: 18.w, |
|||
color: Colors.grey[400], |
|||
), |
|||
), |
|||
), |
|||
), |
|||
Text( |
|||
'解锁 Starter 会员', |
|||
style: TextStyle( |
|||
color: Colors.black87, |
|||
fontSize: 18.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
), |
|||
Padding( |
|||
padding: EdgeInsets.only(top: 5.w, bottom: 20.w), |
|||
child: Text( |
|||
'绑定 VOITRANS 设备可激活 Starter 会员权益,解锁:', |
|||
style: TextStyle( |
|||
color: Colors.grey, |
|||
fontSize: 14.sp, |
|||
), |
|||
), |
|||
), |
|||
_item(Icons.access_time, Colors.red, '每月300分钟转写时长'), |
|||
_item(Icons.description, Colors.purpleAccent, '为行业细分场景设计的专业总结模版'), |
|||
_item(Icons.auto_awesome, Colors.purpleAccent, 'Autopilot 模版自动总结'), |
|||
_item(Icons.people, Colors.red, '区分不同说话人'), |
|||
_item(Icons.cloud_upload, Colors.red, '从APP、WEB导入音频进行转写和总结'), |
|||
Padding( |
|||
padding: EdgeInsets.symmetric(vertical: 20.w), |
|||
child: Row( |
|||
children: [ |
|||
Expanded( |
|||
child: GestureDetector( |
|||
onTap: () { |
|||
Get.to( |
|||
() => const WebViewPage( |
|||
url: 'https://web.voitrans.net', |
|||
title: 'Voitrans', |
|||
), |
|||
); |
|||
}, |
|||
child: Container( |
|||
height: 48.w, |
|||
alignment: Alignment.center, |
|||
decoration: BoxDecoration( |
|||
borderRadius: BorderRadius.circular(12.r), |
|||
border: Border.all( |
|||
color: Colors.grey[400]!, |
|||
), |
|||
), |
|||
child: Text( |
|||
'发现更多', |
|||
style: TextStyle( |
|||
fontSize: 16.sp, |
|||
color: Colors.black, |
|||
), |
|||
), |
|||
), |
|||
), |
|||
), |
|||
10.horizontalSpace, |
|||
Expanded( |
|||
child: GestureDetector( |
|||
onTap: () {}, |
|||
child: Container( |
|||
height: 48.w, |
|||
alignment: Alignment.center, |
|||
decoration: BoxDecoration( |
|||
color: Colors.black, |
|||
borderRadius: BorderRadius.circular(12.r), |
|||
), |
|||
child: Text( |
|||
'立即绑定', |
|||
style: TextStyle( |
|||
fontSize: 16.sp, |
|||
color: Colors.white, |
|||
), |
|||
), |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
Widget _item(IconData icon, Color color, String title) { |
|||
return Padding( |
|||
padding: EdgeInsets.symmetric(vertical: 10.w), |
|||
child: Row( |
|||
children: [ |
|||
Container( |
|||
width: 50.w, |
|||
height: 50.w, |
|||
margin: EdgeInsets.only(right: 10.w), |
|||
decoration: BoxDecoration( |
|||
borderRadius: BorderRadius.circular(16.r), |
|||
border: Border.all( |
|||
color: Colors.red.withOpacity(0.1), |
|||
width: 2.w, |
|||
), |
|||
), |
|||
child: Icon( |
|||
icon, |
|||
size: 24.w, |
|||
color: color, |
|||
), |
|||
), |
|||
Expanded( |
|||
child: Text( |
|||
title, |
|||
style: TextStyle( |
|||
color: Colors.black87, |
|||
fontSize: 14.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,537 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|||
import 'package:get/get.dart'; |
|||
|
|||
import '../../../profile/views/unified_avatar.dart'; |
|||
import '../../controllers/meeting_mine_controller.dart'; |
|||
import '../bottomSheet/member_bottom_sheet.dart'; |
|||
|
|||
class MeetingMemberView extends StatefulWidget { |
|||
const MeetingMemberView({super.key}); |
|||
|
|||
@override |
|||
State<MeetingMemberView> createState() => _MeetingMemberViewState(); |
|||
} |
|||
|
|||
class _MeetingMemberViewState extends State<MeetingMemberView> { |
|||
final _controller = Get.put(MeetingMineController()); |
|||
|
|||
int _tabsIndex = 0; |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Scaffold( |
|||
backgroundColor: Colors.black, |
|||
appBar: _buildAppBar(), |
|||
body: _buildBody(), |
|||
); |
|||
} |
|||
|
|||
// 构建应用栏 |
|||
PreferredSizeWidget _buildAppBar() { |
|||
return AppBar( |
|||
backgroundColor: Colors.black, |
|||
surfaceTintColor: Colors.transparent, |
|||
elevation: 0.5, |
|||
leading: IconButton( |
|||
icon: Icon( |
|||
Icons.arrow_back_ios, |
|||
color: Colors.white, |
|||
size: 20.sp, |
|||
), |
|||
onPressed: () => Get.back(), |
|||
), |
|||
title: Text( |
|||
'VOITRANS AI会员', |
|||
style: TextStyle( |
|||
fontSize: 17.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: Colors.white, |
|||
), |
|||
), |
|||
centerTitle: true, |
|||
); |
|||
} |
|||
|
|||
Widget _buildBody() { |
|||
return Container( |
|||
padding: EdgeInsets.symmetric(horizontal: 12.w), |
|||
child: ListView( |
|||
children: [ |
|||
_head(), |
|||
_member(), |
|||
_tabs(), |
|||
_comparison(), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
Widget _head() { |
|||
return Padding( |
|||
padding: EdgeInsets.symmetric(vertical: 20.w), |
|||
child: Row( |
|||
children: [ |
|||
Container( |
|||
margin: EdgeInsets.only(right: 10.w), |
|||
child: UnifiedAvatar( |
|||
radius: 25.w, |
|||
backgroundColor: Colors.grey[200], |
|||
iconColor: Colors.grey[600], |
|||
iconSize: 25.r, |
|||
isDarkMode: true, |
|||
), |
|||
), |
|||
Expanded( |
|||
child: Obx( |
|||
() => Text( |
|||
_controller.profileCtrl.userInfo.value.name, |
|||
style: TextStyle( |
|||
color: Colors.white, |
|||
fontSize: 20.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
Widget _member() { |
|||
return GestureDetector( |
|||
onTap: () { |
|||
Get.bottomSheet( |
|||
const MemberBottomSheet(), |
|||
isScrollControlled: true, |
|||
backgroundColor: Colors.white, |
|||
shape: RoundedRectangleBorder( |
|||
borderRadius: BorderRadius.vertical( |
|||
top: Radius.circular(12.r), |
|||
), |
|||
), |
|||
); |
|||
}, |
|||
child: Container( |
|||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 15.w), |
|||
decoration: BoxDecoration( |
|||
color: const Color(0xFF1A1A1A), |
|||
borderRadius: BorderRadius.circular(16.w), |
|||
), |
|||
child: Column( |
|||
children: [ |
|||
Row( |
|||
children: [ |
|||
Icon( |
|||
Icons.auto_awesome, |
|||
color: Colors.amberAccent, |
|||
size: 25.sp, |
|||
), |
|||
5.horizontalSpace, |
|||
Text( |
|||
'解锁 Starter 会员', |
|||
style: TextStyle( |
|||
color: Colors.white, |
|||
fontSize: 18.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
), |
|||
], |
|||
), |
|||
Padding( |
|||
padding: EdgeInsets.only(top: 15.w), |
|||
child: Row( |
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|||
children: [ |
|||
Text( |
|||
'绑定 Voitrans 设备可激活 Starter 会员', |
|||
style: TextStyle( |
|||
color: Colors.white, |
|||
fontSize: 12.sp, |
|||
), |
|||
), |
|||
Container( |
|||
padding: EdgeInsets.symmetric( |
|||
horizontal: 10.w, |
|||
vertical: 6.w, |
|||
), |
|||
decoration: BoxDecoration( |
|||
borderRadius: BorderRadius.circular(50.w), |
|||
gradient: const LinearGradient( |
|||
colors: [Colors.blue, Colors.pink], |
|||
begin: Alignment.centerLeft, |
|||
end: Alignment.centerRight, |
|||
), |
|||
), |
|||
child: Text( |
|||
'立即解锁', |
|||
style: TextStyle( |
|||
color: Colors.white, |
|||
fontSize: 14.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
); |
|||
} |
|||
|
|||
Widget _tabs() { |
|||
return Container( |
|||
padding: EdgeInsets.all(2.w), |
|||
margin: EdgeInsets.only(top: 20.w, bottom: 5.w), |
|||
decoration: BoxDecoration( |
|||
color: Colors.grey[700], |
|||
borderRadius: BorderRadius.circular(10.w), |
|||
), |
|||
child: Row( |
|||
children: [ |
|||
Expanded( |
|||
child: GestureDetector( |
|||
onTap: () { |
|||
setState(() { |
|||
_tabsIndex = 0; |
|||
}); |
|||
}, |
|||
behavior: HitTestBehavior.translucent, |
|||
child: Container( |
|||
height: 30.w, |
|||
alignment: Alignment.center, |
|||
decoration: _tabsIndex == 0 |
|||
? BoxDecoration( |
|||
color: Colors.grey[50], |
|||
borderRadius: BorderRadius.circular(8.w), |
|||
) |
|||
: null, |
|||
child: Text( |
|||
'对比', |
|||
style: TextStyle( |
|||
color: _tabsIndex == 0 ? Colors.black : Colors.white, |
|||
fontSize: 14.sp, |
|||
fontWeight: _tabsIndex == 0 ? FontWeight.bold : null, |
|||
), |
|||
), |
|||
), |
|||
), |
|||
), |
|||
Expanded( |
|||
child: GestureDetector( |
|||
onTap: () { |
|||
setState(() { |
|||
_tabsIndex = 1; |
|||
}); |
|||
}, |
|||
behavior: HitTestBehavior.translucent, |
|||
child: Container( |
|||
height: 30.w, |
|||
alignment: Alignment.center, |
|||
decoration: _tabsIndex == 1 |
|||
? BoxDecoration( |
|||
color: Colors.grey[50], |
|||
borderRadius: BorderRadius.circular(8.w), |
|||
) |
|||
: null, |
|||
child: Text( |
|||
'详细信息', |
|||
style: TextStyle( |
|||
color: _tabsIndex == 1 ? Colors.black : Colors.white, |
|||
fontSize: 14.sp, |
|||
fontWeight: _tabsIndex == 1 ? FontWeight.bold : null, |
|||
), |
|||
), |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
Widget _comparison() { |
|||
return Column( |
|||
children: [ |
|||
Container( |
|||
height: 50.w, |
|||
decoration: BoxDecoration( |
|||
border: Border( |
|||
bottom: BorderSide( |
|||
color: Colors.grey[700]!, |
|||
width: 0.5, |
|||
), |
|||
), |
|||
), |
|||
child: Row( |
|||
children: [ |
|||
const Expanded(child: SizedBox.square()), |
|||
Container( |
|||
width: 60.w, |
|||
alignment: Alignment.center, |
|||
child: Text( |
|||
'Starter', |
|||
style: TextStyle( |
|||
color: Colors.grey[200], |
|||
fontSize: 14.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
), |
|||
), |
|||
Container( |
|||
width: 70.w, |
|||
alignment: Alignment.center, |
|||
child: Container( |
|||
width: 40.w, |
|||
height: 24.w, |
|||
alignment: Alignment.center, |
|||
decoration: BoxDecoration( |
|||
borderRadius: BorderRadius.circular(8.w), |
|||
gradient: const LinearGradient( |
|||
colors: [Colors.blue, Colors.pink], |
|||
begin: Alignment.centerLeft, |
|||
end: Alignment.centerRight, |
|||
), |
|||
), |
|||
child: Text( |
|||
'Pro', |
|||
style: TextStyle( |
|||
color: Colors.white, |
|||
fontSize: 14.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
), |
|||
), |
|||
), |
|||
Container( |
|||
width: 72.w, |
|||
alignment: Alignment.center, |
|||
child: Container( |
|||
width: 72.w, |
|||
height: 24.w, |
|||
alignment: Alignment.center, |
|||
decoration: BoxDecoration( |
|||
borderRadius: BorderRadius.circular(8.w), |
|||
gradient: const LinearGradient( |
|||
colors: [ |
|||
Colors.amberAccent, |
|||
Colors.white, |
|||
Colors.amberAccent |
|||
], |
|||
begin: Alignment.centerLeft, |
|||
end: Alignment.centerRight, |
|||
), |
|||
), |
|||
child: Text( |
|||
'Unlimited', |
|||
style: TextStyle( |
|||
color: Colors.black, |
|||
fontSize: 14.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
_tables( |
|||
'每月转写时长', |
|||
'300', |
|||
'1200', |
|||
'无限', |
|||
isGradient: true, |
|||
), |
|||
_tables( |
|||
'专业模版', |
|||
'有限', |
|||
'全量模版', |
|||
'全量模版', |
|||
), |
|||
_tables( |
|||
'专业术语', |
|||
'—', |
|||
'', |
|||
'', |
|||
), |
|||
_tables( |
|||
'自定义模版', |
|||
'—', |
|||
'', |
|||
'', |
|||
), |
|||
_tables( |
|||
'Ask AI', |
|||
'—', |
|||
'', |
|||
'', |
|||
), |
|||
_tables( |
|||
'可视化思维导图', |
|||
'', |
|||
'', |
|||
'', |
|||
), |
|||
_tables( |
|||
'区分说话人', |
|||
'', |
|||
'', |
|||
'', |
|||
), |
|||
_tables( |
|||
'Private Cloud Sync', |
|||
'', |
|||
'', |
|||
'', |
|||
), |
|||
_tables( |
|||
'AI 人声增强', |
|||
'', |
|||
'', |
|||
'', |
|||
), |
|||
_tables( |
|||
'音频导入', |
|||
'', |
|||
'', |
|||
'', |
|||
), |
|||
_tables( |
|||
'支持多种导出格式', |
|||
'', |
|||
'', |
|||
'', |
|||
), |
|||
], |
|||
); |
|||
} |
|||
|
|||
Widget _tables( |
|||
String title, |
|||
String starter, |
|||
String pro, |
|||
String unlimited, { |
|||
bool isGradient = false, |
|||
}) { |
|||
return Container( |
|||
height: 50.w, |
|||
decoration: BoxDecoration( |
|||
border: Border( |
|||
bottom: BorderSide( |
|||
color: Colors.grey[700]!, |
|||
width: 0.5, |
|||
), |
|||
), |
|||
), |
|||
child: Row( |
|||
children: [ |
|||
Expanded( |
|||
child: Text( |
|||
title, |
|||
style: TextStyle( |
|||
color: Colors.grey[200], |
|||
fontSize: 12.sp, |
|||
), |
|||
), |
|||
), |
|||
Container( |
|||
width: 60.w, |
|||
alignment: Alignment.center, |
|||
child: starter.isNotEmpty |
|||
? Text( |
|||
starter, |
|||
style: TextStyle( |
|||
color: Colors.grey[200], |
|||
fontSize: 12.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
) |
|||
: _check(false), |
|||
), |
|||
Container( |
|||
width: 70.w, |
|||
alignment: Alignment.center, |
|||
child: pro.isNotEmpty |
|||
? isGradient |
|||
? ShaderMask( |
|||
shaderCallback: (bounds) => const LinearGradient( |
|||
colors: [Colors.blue, Colors.pink], |
|||
begin: Alignment.centerLeft, |
|||
end: Alignment.centerRight, |
|||
).createShader(bounds), |
|||
child: Text( |
|||
pro, |
|||
style: TextStyle( |
|||
color: Colors.white, |
|||
fontSize: 12.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
), |
|||
) |
|||
: Text( |
|||
pro, |
|||
style: TextStyle( |
|||
color: Colors.white, |
|||
fontSize: 12.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
) |
|||
: _check(isGradient), |
|||
), |
|||
Container( |
|||
width: 72.w, |
|||
alignment: Alignment.center, |
|||
child: unlimited.isNotEmpty |
|||
? ShaderMask( |
|||
shaderCallback: (bounds) => const LinearGradient( |
|||
colors: [ |
|||
Colors.amberAccent, |
|||
Colors.yellowAccent, |
|||
Colors.amberAccent, |
|||
], |
|||
begin: Alignment.centerLeft, |
|||
end: Alignment.centerRight, |
|||
).createShader(bounds), |
|||
child: Text( |
|||
unlimited, |
|||
style: TextStyle( |
|||
color: Colors.white, |
|||
fontSize: 12.sp, |
|||
fontWeight: FontWeight.bold, |
|||
), |
|||
), |
|||
) |
|||
: _check(true), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
Widget _check(bool isGradient) { |
|||
return Container( |
|||
width: 20.w, |
|||
height: 20.w, |
|||
alignment: Alignment.center, |
|||
decoration: BoxDecoration( |
|||
color: Colors.grey[400], |
|||
borderRadius: BorderRadius.circular(20.w), |
|||
gradient: isGradient |
|||
? const LinearGradient( |
|||
colors: [Colors.amberAccent, Colors.white, Colors.amberAccent], |
|||
begin: Alignment.centerLeft, |
|||
end: Alignment.centerRight, |
|||
) |
|||
: null, |
|||
), |
|||
child: Icon( |
|||
Icons.check, |
|||
color: Colors.black, |
|||
size: 16.sp, |
|||
), |
|||
); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue