Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e7304ff19 | |||
| 8c6d1b2add | |||
| 543dd6adb4 | |||
| ec22583ebb | |||
| 96d822cb3f | |||
| ff612bcc56 | |||
| 0b5d510766 | |||
| 666da79fef | |||
| afc58d10a3 | |||
| 4a15c7fa88 | |||
| 5d8dca73d1 | |||
| b7f0fe3ac8 | |||
| 55a1801895 | |||
| 52d775dd78 | |||
| bc98924548 | |||
| 2e9d8c1e0a |
BIN
assets/images/logo.png
Normal file
BIN
assets/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -1,5 +1,6 @@
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class Themes {
|
||||
static final ThemeData light = ThemeData(
|
||||
@@ -12,6 +13,7 @@ class Themes {
|
||||
color: AppColors.active,
|
||||
),
|
||||
foregroundColor: AppColors.active,
|
||||
systemOverlayStyle: SystemUiOverlayStyle.dark,
|
||||
),
|
||||
|
||||
/// 主色调
|
||||
|
||||
@@ -19,8 +19,8 @@ import 'package:get_storage/get_storage.dart';
|
||||
Future<void> main() async {
|
||||
/// 设置状态栏样式,透明
|
||||
SystemUiOverlayStyle style = const SystemUiOverlayStyle(
|
||||
statusBarColor: AppColors.transparent,
|
||||
systemNavigationBarColor: AppColors.nav,
|
||||
statusBarColor: AppColors.transparent, // 顶部状态栏
|
||||
systemNavigationBarColor: AppColors.nav, // 底部多出来那块
|
||||
);
|
||||
SystemChrome.setSystemUIOverlayStyle(style);
|
||||
|
||||
|
||||
@@ -2,38 +2,53 @@ class UserInfoModel {
|
||||
UserInfoModel({
|
||||
required this.userId,
|
||||
required this.username,
|
||||
required this.mobile,
|
||||
required this.email,
|
||||
required this.privacy,
|
||||
required this.nickname,
|
||||
required this.avatar,
|
||||
required this.address,
|
||||
this.google2fa,
|
||||
});
|
||||
|
||||
String userId;
|
||||
String username;
|
||||
String? mobile;
|
||||
String? email;
|
||||
bool? privacy;
|
||||
String? nickname;
|
||||
String? avatar;
|
||||
String? address;
|
||||
bool? google2fa;
|
||||
|
||||
factory UserInfoModel.fromJson(Map<String, dynamic> json) => UserInfoModel(
|
||||
userId: json['user_id'].toString(),
|
||||
username: json['username'],
|
||||
mobile: json['mobile'],
|
||||
email: json['email'],
|
||||
privacy: json['privacy'],
|
||||
nickname: json['nickname'],
|
||||
avatar: json['avatar'],
|
||||
address: json['address'],
|
||||
google2fa: json['google2fa'],
|
||||
);
|
||||
|
||||
factory UserInfoModel.empty() => UserInfoModel(
|
||||
userId: '',
|
||||
username: '',
|
||||
mobile: '',
|
||||
email: '',
|
||||
privacy: true,
|
||||
nickname: '',
|
||||
avatar: '',
|
||||
address: '',
|
||||
google2fa: false,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"userId": userId,
|
||||
"username": username,
|
||||
"nickname": nickname,
|
||||
"avatar": avatar,
|
||||
"address": address,
|
||||
'userId': userId,
|
||||
'username': username,
|
||||
'mobile': mobile,
|
||||
'email': email,
|
||||
'privacy': privacy,
|
||||
'nickname': nickname,
|
||||
'avatar': avatar,
|
||||
'google2fa': google2fa,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@ import 'package:chat/utils/network/http.dart';
|
||||
import 'package:chat/utils/ui_tools.dart';
|
||||
|
||||
class AuthProvider {
|
||||
static Future<AuthModel?> login(String address) async {
|
||||
static Future<AuthModel?> login(String address, String mnemonic) async {
|
||||
try {
|
||||
final result = await Http.post(
|
||||
'auth/login',
|
||||
data: {
|
||||
'address': address,
|
||||
'mnemonic': mnemonic,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
25
lib/providers/public_provider.dart
Normal file
25
lib/providers/public_provider.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:chat/utils/network/http.dart';
|
||||
import 'package:chat/utils/ui_tools.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
class PublicProvider {
|
||||
/// 检测更新
|
||||
static Future checkUpgrade(PackageInfo? packageInfo) async {
|
||||
try {
|
||||
if (packageInfo == null) {
|
||||
return null;
|
||||
} else {
|
||||
return await Http.get(
|
||||
'version/check',
|
||||
params: {
|
||||
'package_name': packageInfo.packageName,
|
||||
'version': packageInfo.version,
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
UiTools.toast(e.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -49,8 +49,44 @@ class UserProvider {
|
||||
(x) => SearchUserModel.fromJson(x),
|
||||
),
|
||||
);
|
||||
} catch (err) {
|
||||
UiTools.toast(err.toString());
|
||||
} catch (e) {
|
||||
UiTools.toast(e.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<bool?> togglePrivacy() async {
|
||||
try {
|
||||
return await Http.put('user/privacy');
|
||||
} catch (e) {
|
||||
UiTools.toast(e.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<String?> downloadUrl() async {
|
||||
try {
|
||||
return await Http.get('user/download');
|
||||
} catch (e) {
|
||||
UiTools.toast(e.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future updateMobile(String mobile) async {
|
||||
try {
|
||||
return await Http.post('user/mobile');
|
||||
} catch (e) {
|
||||
UiTools.toast(e.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future updateEmail(String email) async {
|
||||
try {
|
||||
return await Http.post('user/email');
|
||||
} catch (e) {
|
||||
UiTools.toast(e.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:chat/views/home/index_page.dart';
|
||||
import 'package:chat/views/public/app_page.dart';
|
||||
import 'package:chat/views/public/scan_page.dart';
|
||||
import 'package:chat/views/public/result_page.dart';
|
||||
import 'package:chat/views/public/scan/index_page.dart';
|
||||
import 'package:chat/views/public/transit_page.dart';
|
||||
import 'package:chat/views/search/index_page.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -12,6 +13,7 @@ abstract class AppRoutes {
|
||||
static const String transit = '/transit';
|
||||
static const String notfound = '/notfound';
|
||||
static const String scan = '/scan';
|
||||
static const String scanResult = '/scan/result';
|
||||
|
||||
static const String home = '/home';
|
||||
static const String search = '/search';
|
||||
@@ -27,6 +29,12 @@ abstract class AppRoutes {
|
||||
GetPage(
|
||||
name: AppRoutes.scan,
|
||||
page: () => const ScanPage(),
|
||||
children: [
|
||||
GetPage(
|
||||
name: '/result',
|
||||
page: () => const ScanResultPage(),
|
||||
),
|
||||
],
|
||||
),
|
||||
GetPage(
|
||||
name: AppRoutes.home,
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import 'package:chat/middleware/auth_middleware.dart';
|
||||
import 'package:chat/views/contact/index/index_page.dart';
|
||||
import 'package:chat/views/user/info/avatar_page.dart';
|
||||
import 'package:chat/views/user/info/index_page.dart';
|
||||
import 'package:chat/views/user/info/nickname_page.dart';
|
||||
import 'package:chat/views/user/qr_code/index_page.dart';
|
||||
import 'package:chat/views/user/safe/index_page.dart';
|
||||
import 'package:chat/views/user/serve/google/index_page.dart';
|
||||
import 'package:chat/views/user/serve/index_page.dart';
|
||||
import 'package:chat/views/user/setting/about/index_page.dart';
|
||||
import 'package:chat/views/user/setting/index_page.dart';
|
||||
import 'package:chat/views/user/setting/message/index_page.dart';
|
||||
import 'package:chat/views/user/setting/privacy/index_page.dart';
|
||||
import 'package:chat/views/user/setting/safe/email_page.dart';
|
||||
import 'package:chat/views/user/setting/safe/google_page.dart';
|
||||
import 'package:chat/views/user/setting/safe/index_page.dart';
|
||||
import 'package:chat/views/user/setting/safe/mobile_page.dart';
|
||||
import 'package:chat/views/user/setting/sugguest/index_page.dart';
|
||||
import 'package:chat/views/user/share/index_page.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -13,12 +22,25 @@ abstract class UserRoutes {
|
||||
/// 身份验证页面
|
||||
static const String index = '/user';
|
||||
static const String qrCode = '/user/qrCode';
|
||||
|
||||
static const String setting = '/user/setting';
|
||||
static const String settingAbout = '/user/setting/about';
|
||||
static const String settingMessage = '/user/setting/message';
|
||||
static const String settingPrivacy = '/user/setting/privacy';
|
||||
static const String settingSafe = '/user/setting/safe';
|
||||
static const String settingSafeEmail = '/user/setting/safe/email';
|
||||
static const String settingSafeGoogle = '/user/setting/safe/google';
|
||||
static const String settingSafeMobile = '/user/setting/safe/mobile';
|
||||
static const String settingSugguest = '/user/setting/sugguest';
|
||||
|
||||
static const String share = '/user/share';
|
||||
static const String safe = '/user/safe';
|
||||
|
||||
static const String info = '/user/info';
|
||||
static const String infoAvatar = '/user/info/avatar';
|
||||
static const String infoNickname = '/user/info/nickname';
|
||||
|
||||
static const String serve = '/user/serve';
|
||||
static const String serveGoogle = '/user/serve/google';
|
||||
|
||||
static GetPage router = GetPage(
|
||||
name: UserRoutes.index,
|
||||
@@ -38,15 +60,51 @@ abstract class UserRoutes {
|
||||
GetPage(
|
||||
name: '/setting',
|
||||
page: () => const UserSettingPage(),
|
||||
children: [
|
||||
GetPage(
|
||||
name: '/about',
|
||||
page: () => const UserSettingAboutPage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/message',
|
||||
page: () => const UserSettingMessagePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/privacy',
|
||||
page: () => const UserSettingPrivacyPage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/safe',
|
||||
page: () => const UserSafePage(),
|
||||
page: () => const UserSettingSafePage(),
|
||||
children: [
|
||||
GetPage(
|
||||
name: '/email',
|
||||
page: () => const UserSettingSafeEmailPage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/google',
|
||||
page: () => const UserSettingSafeGooglePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/mobile',
|
||||
page: () => const UserSettingSafeMobilePage(),
|
||||
),
|
||||
],
|
||||
),
|
||||
GetPage(
|
||||
name: '/sugguest',
|
||||
page: () => const UserSettingSugguestPage(),
|
||||
),
|
||||
],
|
||||
),
|
||||
GetPage(
|
||||
name: '/info',
|
||||
page: () => const UserInfoPage(),
|
||||
children: [
|
||||
GetPage(
|
||||
name: '/avatar',
|
||||
page: () => const UserInfoAvatarPage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/nickname',
|
||||
page: () => const UserInfoNicknamePage(),
|
||||
@@ -56,6 +114,12 @@ abstract class UserRoutes {
|
||||
GetPage(
|
||||
name: '/serve',
|
||||
page: () => const UserServePage(),
|
||||
children: [
|
||||
GetPage(
|
||||
name: '/google',
|
||||
page: () => const UserServeGooglePage(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import 'package:chat/models/user_info_model.dart';
|
||||
import 'package:chat/providers/auth_provider.dart';
|
||||
import 'package:chat/providers/user_provider.dart';
|
||||
import 'package:chat/routes/auth_routes.dart';
|
||||
import 'package:chat/services/tim/apply_service.dart';
|
||||
import 'package:chat/services/tim/block_service.dart';
|
||||
import 'package:chat/services/tim/conversation_service.dart';
|
||||
import 'package:chat/services/tim/friend_service.dart';
|
||||
import 'package:chat/services/tim/group_service.dart';
|
||||
import 'package:chat/services/tim/message_service.dart';
|
||||
import 'package:chat/services/tim_service.dart';
|
||||
import 'package:chat/utils/hd_wallet.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -28,6 +35,7 @@ class AuthService extends GetxService {
|
||||
String get _userSig => _box.read('userSig') ?? '';
|
||||
String get _userToken => _box.read('userToken') ?? '';
|
||||
Map<String, dynamic> get _userInfo => _box.read('userInfo') ?? {};
|
||||
String get mnemonic => _box.read('mnemonic') ?? '';
|
||||
|
||||
/// 用户信息,这个数据,在更新用户资料的时候,也应该更新
|
||||
Rx<UserInfoModel> userInfo = UserInfoModel.empty().obs;
|
||||
@@ -52,9 +60,10 @@ class AuthService extends GetxService {
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = await AuthProvider.login(address);
|
||||
var result = await AuthProvider.login(address, mnemonic);
|
||||
|
||||
if (result != null) {
|
||||
_box.write('mnemonic', mnemonic);
|
||||
_box.write('userId', result.userID);
|
||||
_box.write('userSig', result.userSig);
|
||||
_box.write('userToken', result.userToken);
|
||||
@@ -82,12 +91,23 @@ class AuthService extends GetxService {
|
||||
_box.remove('userSig');
|
||||
_box.remove('userId');
|
||||
_box.remove('userToken');
|
||||
_box.remove('mnemonic');
|
||||
_box.remove('userInfo');
|
||||
|
||||
userSig = '';
|
||||
userId = '';
|
||||
userToken = '';
|
||||
|
||||
TimService.to.instance.logout();
|
||||
|
||||
/// 移除这些服务
|
||||
Get.delete<TimConversationService>();
|
||||
Get.delete<TimFriendService>();
|
||||
Get.delete<TimGroupService>();
|
||||
Get.delete<TimBlockService>();
|
||||
Get.delete<TimApplyService>();
|
||||
Get.delete<TimMessageService>();
|
||||
|
||||
userInfo.value = UserInfoModel.empty();
|
||||
isLogin.value = false;
|
||||
Get.offAllNamed(AuthRoutes.index);
|
||||
@@ -104,4 +124,14 @@ class AuthService extends GetxService {
|
||||
|
||||
_box.write('userInfo', userInfo.toJson());
|
||||
}
|
||||
|
||||
/// 更新隐私状态
|
||||
Future<void> togglePrivacy() async {
|
||||
var res = await UserProvider.togglePrivacy();
|
||||
if (res != null) {
|
||||
userInfo.value.privacy = res;
|
||||
userInfo.refresh();
|
||||
}
|
||||
_box.write('userInfo', userInfo.toJson());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,12 @@ class TimFriendService extends GetxService {
|
||||
),
|
||||
];
|
||||
|
||||
@override
|
||||
void onInit() async {
|
||||
super.onInit();
|
||||
await fetchList();
|
||||
}
|
||||
|
||||
Future<void> fetchList() async {
|
||||
var result = await friendshipManager.getFriendList();
|
||||
if (result.code == 0) {
|
||||
|
||||
@@ -210,7 +210,7 @@ class ImTools {
|
||||
}
|
||||
|
||||
static showTrtcMessage(String userID) {
|
||||
return showModalBottomSheet(
|
||||
showModalBottomSheet(
|
||||
context: Get.context!,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: AppColors.white,
|
||||
|
||||
@@ -66,9 +66,9 @@ class ContactFriendProfilePage extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 0),
|
||||
ActionItem(
|
||||
'设置备注',
|
||||
isLast: true,
|
||||
extend: _.currentFriend.value.friendRemark,
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
@@ -76,9 +76,11 @@ class ContactFriendProfilePage extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 9),
|
||||
ActionItem(
|
||||
'他的动态',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
MomentsRoutes.user,
|
||||
@@ -93,6 +95,8 @@ class ContactFriendProfilePage extends StatelessWidget {
|
||||
visible: !_.currentFriend.value.isFriend,
|
||||
child: ActionButton(
|
||||
'添加到通讯录',
|
||||
hasTop: true,
|
||||
hasBottom: true,
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
ContactRoutes.friendRequestApply,
|
||||
@@ -107,7 +111,8 @@ class ContactFriendProfilePage extends StatelessWidget {
|
||||
visible: _.currentFriend.value.isFriend,
|
||||
child: ActionButton(
|
||||
'发消息',
|
||||
color: AppColors.primary,
|
||||
hasTop: true,
|
||||
hasBottom: true,
|
||||
onTap: () async {
|
||||
var result = await TimConversationService
|
||||
.to.conversationManager
|
||||
@@ -125,15 +130,11 @@ class ContactFriendProfilePage extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: _.currentFriend.value.isFriend,
|
||||
child: const Divider(height: 0),
|
||||
),
|
||||
Visibility(
|
||||
visible: _.currentFriend.value.isFriend,
|
||||
child: ActionButton(
|
||||
'音视频通话',
|
||||
color: AppColors.primary,
|
||||
hasBottom: true,
|
||||
onTap: () {
|
||||
ImTools.showTrtcMessage(_.currentFriend.value.userID);
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:chat/controllers/private_controller.dart';
|
||||
import 'package:chat/routes/contact_routes.dart';
|
||||
import 'package:chat/services/tim/block_service.dart';
|
||||
@@ -23,6 +24,8 @@ class ContactFriendProfileMorePage extends StatelessWidget {
|
||||
children: [
|
||||
ActionItem(
|
||||
'设置备注',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
extend: _.currentFriend.value.friendRemark,
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
@@ -33,6 +36,8 @@ class ContactFriendProfileMorePage extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
ActionItem(
|
||||
'把他推荐给朋友',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
ContactRoutes.friendRecommend,
|
||||
@@ -42,6 +47,8 @@ class ContactFriendProfileMorePage extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
ActionItem(
|
||||
'加入黑名单',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
rightWidget: SizedBox(
|
||||
height: 24,
|
||||
child: Switch(
|
||||
@@ -74,6 +81,9 @@ class ContactFriendProfileMorePage extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
ActionButton(
|
||||
'删除',
|
||||
hasTop: true,
|
||||
hasBottom: true,
|
||||
color: AppColors.red,
|
||||
onTap: () async {
|
||||
OkCancelResult result = await showOkCancelAlertDialog(
|
||||
style: AdaptiveStyle.iOS,
|
||||
|
||||
@@ -3,8 +3,11 @@ import 'package:chat/controllers/private_controller.dart';
|
||||
import 'package:chat/models/im/search_user_model.dart';
|
||||
import 'package:chat/providers/user_provider.dart';
|
||||
import 'package:chat/routes/contact_routes.dart';
|
||||
import 'package:chat/routes/user_routes.dart';
|
||||
import 'package:chat/services/auth_service.dart';
|
||||
import 'package:chat/services/tim/apply_service.dart';
|
||||
import 'package:chat/services/tim/friend_service.dart';
|
||||
import 'package:chat/utils/convert.dart';
|
||||
import 'package:chat/widgets/custom_avatar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -34,7 +37,7 @@ class _ImFriendRequestState extends State<ContactFriendRequestPage> {
|
||||
title: const Text('新的朋友'),
|
||||
bottom: _Search(
|
||||
onChanged: (String e) async {
|
||||
if (e.length < 3) {
|
||||
if (e.isEmpty) {
|
||||
setState(() {
|
||||
searchList = null;
|
||||
});
|
||||
@@ -232,7 +235,7 @@ class _Search extends StatelessWidget implements PreferredSizeWidget {
|
||||
child: TextField(
|
||||
keyboardType: TextInputType.phone,
|
||||
decoration: InputDecoration(
|
||||
hintText: '请输入对方手机号',
|
||||
hintText: '请输入对方的区块链地址',
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.unactive,
|
||||
@@ -266,19 +269,24 @@ class _Search extends StatelessWidget implements PreferredSizeWidget {
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Get.toNamed(ContactRoutes.friendProfile);
|
||||
Get.toNamed(UserRoutes.qrCode);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [
|
||||
children: [
|
||||
Text(
|
||||
'我的二维码: ',
|
||||
style: TextStyle(
|
||||
'我的地址: ' +
|
||||
Convert.hideCenterStr(
|
||||
AuthService.to.userInfo.value.username,
|
||||
start: 4,
|
||||
end: 4,
|
||||
),
|
||||
style: const TextStyle(
|
||||
color: AppColors.unactive,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Icon(
|
||||
const SizedBox(width: 8),
|
||||
const Icon(
|
||||
Icons.qr_code,
|
||||
size: 18,
|
||||
color: AppColors.unactive,
|
||||
|
||||
@@ -9,20 +9,9 @@ import 'package:chat/widgets/custom_avatar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ContactPage extends StatefulWidget {
|
||||
class ContactPage extends StatelessWidget {
|
||||
const ContactPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ContactPage> createState() => _ContactPageState();
|
||||
}
|
||||
|
||||
class _ContactPageState extends State<ContactPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
TimFriendService.to.fetchList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
||||
@@ -37,6 +37,7 @@ class ConversationInfoGroupPage extends StatelessWidget {
|
||||
children: [
|
||||
ActionItem(
|
||||
'群聊名称',
|
||||
isFirst: true,
|
||||
extend: group?.groupName,
|
||||
onTap: () {
|
||||
if (currentGroup.isAdmin || currentGroup.isOwner) {
|
||||
@@ -46,7 +47,6 @@ class ConversationInfoGroupPage extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
),
|
||||
const Divider(height: 0, indent: 16),
|
||||
ActionItem(
|
||||
'群二维码',
|
||||
onTap: () {
|
||||
@@ -55,7 +55,6 @@ class ConversationInfoGroupPage extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
const Divider(height: 0, indent: 16),
|
||||
ActionItem(
|
||||
'群公告',
|
||||
bottom: group?.notification,
|
||||
@@ -65,13 +64,6 @@ class ConversationInfoGroupPage extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
Visibility(
|
||||
visible: currentGroup.isAdmin || currentGroup.isOwner,
|
||||
child: const Divider(
|
||||
height: 0,
|
||||
indent: 16,
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: currentGroup.isAdmin || currentGroup.isOwner,
|
||||
child: ActionItem(
|
||||
@@ -83,17 +75,11 @@ class ConversationInfoGroupPage extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: currentGroup.isAdmin || currentGroup.isOwner,
|
||||
child: const Divider(
|
||||
height: 0,
|
||||
indent: 16,
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: currentGroup.isAdmin || currentGroup.isOwner,
|
||||
child: ActionItem(
|
||||
'加群申请',
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
ContactRoutes.groupApprove,
|
||||
@@ -140,10 +126,6 @@ class ConversationInfoGroupPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
indent: 16,
|
||||
),
|
||||
ActionItem(
|
||||
'置顶聊天',
|
||||
rightWidget: SizedBox(
|
||||
|
||||
@@ -80,22 +80,21 @@ class ConversationInfoPrivatePage extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 0),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(height: 0),
|
||||
ActionItem(
|
||||
'查找聊天记录',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
// Get.toNamed(
|
||||
// ImRoutes.conversationSearch,
|
||||
// );
|
||||
},
|
||||
),
|
||||
const Divider(height: 0),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(height: 0),
|
||||
ActionItem(
|
||||
'消息免打扰',
|
||||
isFirst: true,
|
||||
rightWidget: SizedBox(
|
||||
height: 24,
|
||||
child: Switch(
|
||||
@@ -107,12 +106,9 @@ class ConversationInfoPrivatePage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
indent: 16,
|
||||
),
|
||||
ActionItem(
|
||||
'置顶聊天',
|
||||
isLast: true,
|
||||
rightWidget: SizedBox(
|
||||
height: 24,
|
||||
child: Switch(
|
||||
@@ -124,11 +120,11 @@ class ConversationInfoPrivatePage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(height: 0),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(height: 0),
|
||||
ActionItem(
|
||||
'清空聊天记录',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
onTap: () async {
|
||||
OkCancelResult result = await showOkCancelAlertDialog(
|
||||
style: AdaptiveStyle.iOS,
|
||||
@@ -147,7 +143,6 @@ class ConversationInfoPrivatePage extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
),
|
||||
const Divider(height: 0),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -27,7 +27,10 @@ class HomePage extends StatelessWidget {
|
||||
child: GetX<TimConversationService>(
|
||||
builder: (_) {
|
||||
return _.conversationList.isEmpty
|
||||
? CustomEasyRefresh.empty(text: '')
|
||||
? ConstrainedBox(
|
||||
constraints: BoxConstraints(minHeight: Get.height / 2),
|
||||
child: CustomEasyRefresh.empty(text: '暂无消息'),
|
||||
)
|
||||
: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
@@ -68,7 +71,7 @@ class HomePage extends StatelessWidget {
|
||||
Get.toNamed(ContactRoutes.groupCreate);
|
||||
break;
|
||||
case 'C':
|
||||
Get.toNamed(ContactRoutes.friendSearch);
|
||||
Get.toNamed(ContactRoutes.friendRequest);
|
||||
break;
|
||||
case 'D':
|
||||
Permission.camera.request().isGranted.then((value) {
|
||||
|
||||
@@ -5,10 +5,14 @@ class ActionButton extends StatelessWidget {
|
||||
final String text;
|
||||
final Color color;
|
||||
final VoidCallback? onTap;
|
||||
final bool hasTop;
|
||||
final bool hasBottom;
|
||||
const ActionButton(
|
||||
this.text, {
|
||||
this.color = AppColors.red,
|
||||
this.color = AppColors.primary,
|
||||
this.onTap,
|
||||
this.hasTop = false,
|
||||
this.hasBottom = false,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -19,14 +23,22 @@ class ActionButton extends StatelessWidget {
|
||||
onTap: () {
|
||||
onTap?.call();
|
||||
},
|
||||
child: Container(
|
||||
child: Column(
|
||||
children: [
|
||||
if (hasTop)
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
Container(
|
||||
color: AppColors.white,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
padding: const EdgeInsets.all(14.0),
|
||||
child: Center(
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: color,
|
||||
),
|
||||
@@ -34,6 +46,13 @@ class ActionButton extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (hasBottom)
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ class ActionItem extends StatelessWidget {
|
||||
final Widget? rightWidget;
|
||||
final String? bottom;
|
||||
final VoidCallback? onTap;
|
||||
final bool isFirst;
|
||||
final bool isLast;
|
||||
final double indent;
|
||||
|
||||
const ActionItem(
|
||||
this.title, {
|
||||
@@ -14,6 +17,9 @@ class ActionItem extends StatelessWidget {
|
||||
this.rightWidget,
|
||||
this.bottom,
|
||||
this.onTap,
|
||||
this.isFirst = false,
|
||||
this.isLast = false,
|
||||
this.indent = 16,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -24,9 +30,18 @@ class ActionItem extends StatelessWidget {
|
||||
onTap: () {
|
||||
onTap?.call();
|
||||
},
|
||||
child: Container(
|
||||
color: AppColors.white,
|
||||
child: Column(
|
||||
children: [
|
||||
if (isFirst)
|
||||
const Divider(
|
||||
height: 0.4,
|
||||
color: AppColors.border,
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.white,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -71,6 +86,19 @@ class ActionItem extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isLast)
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
if (!isLast)
|
||||
Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
indent: indent,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,33 +8,33 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyrefresh/easy_refresh.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MomentsPage extends StatelessWidget {
|
||||
class MomentsPage extends GetView<MomentController> {
|
||||
const MomentsPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ctrl = MomentController.to;
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewPadding.bottom,
|
||||
),
|
||||
child: EasyRefresh.custom(
|
||||
scrollController: ctrl.scrollController,
|
||||
controller: ctrl.refreshController,
|
||||
scrollController: controller.scrollController,
|
||||
controller: controller.refreshController,
|
||||
header: LinkHeader(
|
||||
ctrl.headerNotifier,
|
||||
controller.headerNotifier,
|
||||
extent: 70.0,
|
||||
triggerDistance: 70.0,
|
||||
completeDuration: const Duration(milliseconds: 500),
|
||||
),
|
||||
footer: CustomEasyRefresh.footer,
|
||||
onRefresh: () => ctrl.refreshList(),
|
||||
onLoad: () => ctrl.loadMoreList(),
|
||||
onRefresh: () => controller.refreshList(),
|
||||
onLoad: () => controller.loadMoreList(),
|
||||
slivers: [
|
||||
MomentHeader(
|
||||
linkNotifier: ctrl.headerNotifier,
|
||||
linkNotifier: controller.headerNotifier,
|
||||
onTitleDoubleTap: () {
|
||||
ctrl.scrollController.animateTo(
|
||||
controller.scrollController.animateTo(
|
||||
0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
@@ -42,7 +42,7 @@ class MomentsPage extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
Obx(() {
|
||||
final momentList = ctrl.momentData.value?.data ?? [];
|
||||
final momentList = controller.momentData.value?.data ?? [];
|
||||
if (momentList.isEmpty) {
|
||||
return SliverFillRemaining(
|
||||
child: CustomEasyRefresh.empty(text: '暂无动态内容'),
|
||||
@@ -76,7 +76,7 @@ class MomentsPage extends StatelessWidget {
|
||||
child: MomentListItem(item: item),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 50),
|
||||
padding: const EdgeInsets.only(left: 52),
|
||||
child: MomentListItemReplay(
|
||||
index: index,
|
||||
item: item,
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MomentAvatar extends StatelessWidget {
|
||||
final String imageUrl;
|
||||
const MomentAvatar({Key? key, String? imageUrl})
|
||||
: imageUrl = imageUrl ?? '',
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||
color: AppColors.white,
|
||||
image: imageUrl.isNotEmpty
|
||||
? DecorationImage(
|
||||
image: CachedNetworkImageProvider(imageUrl),
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import 'package:chat/routes/moments_routes.dart';
|
||||
import 'package:chat/services/auth_service.dart';
|
||||
import 'package:chat/widgets/custom_avatar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_easyrefresh/easy_refresh.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -21,7 +20,7 @@ class MomentHeader extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverAppBar(
|
||||
systemOverlayStyle: SystemUiOverlayStyle.light,
|
||||
// systemOverlayStyle: SystemUiOverlayStyle.light,
|
||||
pinned: true,
|
||||
expandedHeight: 260,
|
||||
backgroundColor: AppColors.primary,
|
||||
@@ -80,8 +79,9 @@ class _HeaderBackground extends StatelessWidget {
|
||||
Positioned.fill(
|
||||
bottom: 32,
|
||||
child: GestureDetector(
|
||||
onLongPress: () {},
|
||||
child: Image.asset(
|
||||
'assets/backgrounds/moment_2.jpg',
|
||||
'assets/backgrounds/moment_3.jpg',
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:chat/controllers/moment_controller.dart';
|
||||
import 'package:chat/models/moment/moment_model.dart';
|
||||
import 'package:chat/views/moments/index/widgets/future_button.dart';
|
||||
import 'package:chat/views/moments/index/widgets/grid_media.dart';
|
||||
import 'package:chat/views/moments/index/widgets/moment_avatar.dart';
|
||||
import 'package:chat/widgets/custom_avatar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -46,7 +46,7 @@ class MomentListItem extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
MomentAvatar(imageUrl: item.user?.avatar),
|
||||
CustomAvatar(item.user?.avatar),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
|
||||
48
lib/views/public/result_page.dart
Normal file
48
lib/views/public/result_page.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:chat/utils/ui_tools.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ScanResultPage extends StatelessWidget {
|
||||
const ScanResultPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String content = Get.arguments;
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.white,
|
||||
appBar: AppBar(
|
||||
title: const Text('扫码结果'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: content,
|
||||
),
|
||||
);
|
||||
UiTools.toast('复制成功');
|
||||
},
|
||||
child: const Text('复制结果'),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
children: [
|
||||
Text(
|
||||
content,
|
||||
style: const TextStyle(
|
||||
height: 1.5,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
144
lib/views/public/scan/index_page.dart
Normal file
144
lib/views/public/scan/index_page.dart
Normal file
@@ -0,0 +1,144 @@
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:chat/routes/app_routes.dart';
|
||||
import 'package:chat/routes/user_routes.dart';
|
||||
import 'package:chat/utils/ui_tools.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:scan/scan.dart';
|
||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||
|
||||
class ScanPage extends StatefulWidget {
|
||||
const ScanPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ScanPageState createState() => _ScanPageState();
|
||||
}
|
||||
|
||||
class _ScanPageState extends State<ScanPage> {
|
||||
final ScanController _scanController = ScanController();
|
||||
|
||||
bool flashState = false;
|
||||
|
||||
/// 统一处理扫码结果,可能是扫码的,也可能是相册的
|
||||
void parseScanResult(String str) {
|
||||
List<String> split = str.split('|');
|
||||
|
||||
switch (split.first) {
|
||||
case 'TRANSFER':
|
||||
break;
|
||||
case 'BEFRIEND':
|
||||
break;
|
||||
case 'JOINGROUP':
|
||||
break;
|
||||
default:
|
||||
Get.offNamed(
|
||||
AppRoutes.scanResult,
|
||||
arguments: str,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
ScanView(
|
||||
controller: _scanController,
|
||||
scanAreaScale: 0.7,
|
||||
scanLineColor: AppColors.primary,
|
||||
onCapture: (String data) {
|
||||
parseScanResult(data);
|
||||
},
|
||||
),
|
||||
AppBar(
|
||||
backgroundColor: AppColors.transparent,
|
||||
foregroundColor: AppColors.white,
|
||||
),
|
||||
Positioned(
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 32,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_actionItem(
|
||||
'我的',
|
||||
Icons.qr_code,
|
||||
() {
|
||||
Get.toNamed(UserRoutes.qrCode);
|
||||
},
|
||||
),
|
||||
_actionItem(
|
||||
'手电筒',
|
||||
flashState ? Icons.flash_on : Icons.flash_off,
|
||||
() {
|
||||
setState(() {
|
||||
_scanController.toggleTorchMode();
|
||||
flashState = !flashState;
|
||||
});
|
||||
},
|
||||
),
|
||||
_actionItem(
|
||||
'相册',
|
||||
Icons.image,
|
||||
() async {
|
||||
final result = await AssetPicker.pickAssets(
|
||||
Get.context!,
|
||||
pickerConfig: const AssetPickerConfig(
|
||||
maxAssets: 1,
|
||||
requestType: RequestType.image,
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var text = await Scan.parse((await result.first.file)!.path);
|
||||
|
||||
if (text == null) {
|
||||
UiTools.toast('二维码识别失败');
|
||||
}
|
||||
|
||||
parseScanResult(text!);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _actionItem(String text, IconData icon, VoidCallback onTap) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
onTap.call();
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white.withOpacity(0.4),
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
),
|
||||
child: Icon(
|
||||
icon,
|
||||
color: AppColors.white,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
color: AppColors.white,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ScanPage extends StatefulWidget {
|
||||
const ScanPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ScanPageState createState() => _ScanPageState();
|
||||
}
|
||||
|
||||
class _ScanPageState extends State<ScanPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
@@ -1,73 +1,44 @@
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:chat/routes/user_routes.dart';
|
||||
import 'package:chat/views/home/widgets/action_item.dart';
|
||||
import 'package:chat/views/user/index/widgets/user_top_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class UserPage extends StatefulWidget {
|
||||
class UserPage extends StatelessWidget {
|
||||
const UserPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_UserPageState createState() => _UserPageState();
|
||||
}
|
||||
|
||||
class _UserPageState extends State<UserPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: [
|
||||
const UserTopBar(),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
ActionItem(
|
||||
'服务',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.serve);
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
ActionItem(
|
||||
'分享邀请',
|
||||
isFirst: true,
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.share);
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
indent: 16,
|
||||
),
|
||||
ActionItem(
|
||||
'设置',
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.setting);
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,18 +11,25 @@ import 'package:get/get.dart';
|
||||
class UserTopBar extends StatelessWidget {
|
||||
const UserTopBar({Key? key}) : super(key: key);
|
||||
|
||||
final double paddingTop = 96;
|
||||
final double paddingBottom = 24;
|
||||
final double avatarHeight = 64;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
color: AppColors.white,
|
||||
padding: const EdgeInsets.only(
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
top: 48,
|
||||
top: paddingTop,
|
||||
right: 24,
|
||||
bottom: 24,
|
||||
bottom: paddingBottom,
|
||||
),
|
||||
height: 64 + 48 + 24,
|
||||
child: GetX<AuthService>(builder: (_) {
|
||||
height: avatarHeight + paddingTop + paddingBottom,
|
||||
child: GetX<AuthService>(
|
||||
builder: (_) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
@@ -32,7 +39,7 @@ class UserTopBar extends StatelessWidget {
|
||||
},
|
||||
child: CustomAvatar(
|
||||
_.userInfo.value.avatar,
|
||||
size: 64,
|
||||
size: avatarHeight,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
@@ -55,7 +62,7 @@ class UserTopBar extends StatelessWidget {
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
Convert.hideCenterStr(_.userInfo.value.address ?? ''),
|
||||
Convert.hideCenterStr(_.userInfo.value.username),
|
||||
style: const TextStyle(
|
||||
color: AppColors.unactive,
|
||||
),
|
||||
@@ -65,7 +72,7 @@ class UserTopBar extends StatelessWidget {
|
||||
onTap: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: _.userInfo.value.address,
|
||||
text: _.userInfo.value.username,
|
||||
),
|
||||
);
|
||||
UiTools.toast('地址复制成功');
|
||||
@@ -103,7 +110,14 @@ class UserTopBar extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
},
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
127
lib/views/user/info/avatar_page.dart
Normal file
127
lib/views/user/info/avatar_page.dart
Normal file
@@ -0,0 +1,127 @@
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:chat/controllers/user_controller.dart';
|
||||
import 'package:chat/services/auth_service.dart';
|
||||
import 'package:chat/views/home/widgets/pop_menu_item.dart';
|
||||
import 'package:chat/widgets/custom_avatar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||
|
||||
class UserInfoAvatarPage extends StatelessWidget {
|
||||
const UserInfoAvatarPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.black,
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'头像',
|
||||
style: TextStyle(
|
||||
color: AppColors.white,
|
||||
),
|
||||
),
|
||||
backgroundColor: AppColors.transparent,
|
||||
foregroundColor: AppColors.white,
|
||||
systemOverlayStyle: SystemUiOverlayStyle.light,
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: _showMenu,
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: GetX<AuthService>(
|
||||
builder: (_) {
|
||||
return CustomAvatar(
|
||||
_.userInfo.value.avatar,
|
||||
size: Get.width,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showMenu() async {
|
||||
showModalBottomSheet(
|
||||
context: Get.context!,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: AppColors.white,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
|
||||
),
|
||||
builder: (context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
PopMenuItem(
|
||||
'从手机相册选择',
|
||||
onTap: _pickImage,
|
||||
),
|
||||
const Divider(height: 0),
|
||||
Container(
|
||||
color: AppColors.page,
|
||||
height: 8,
|
||||
),
|
||||
const Divider(height: 0),
|
||||
PopMenuItem(
|
||||
'取消',
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future _pickImage() async {
|
||||
Get.back();
|
||||
final result = await AssetPicker.pickAssets(
|
||||
Get.context!,
|
||||
pickerConfig: const AssetPickerConfig(
|
||||
maxAssets: 1,
|
||||
textDelegate: AssetPickerTextDelegate(),
|
||||
requestType: RequestType.image,
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
_cropImage((await result.first.file)!.path);
|
||||
}
|
||||
|
||||
Future<void> _cropImage(String imagePath) async {
|
||||
CroppedFile? croppedFile = await ImageCropper().cropImage(
|
||||
sourcePath: imagePath,
|
||||
maxHeight: 512,
|
||||
maxWidth: 512,
|
||||
compressQuality: 90,
|
||||
aspectRatio: const CropAspectRatio(
|
||||
ratioX: 1,
|
||||
ratioY: 1,
|
||||
),
|
||||
compressFormat: ImageCompressFormat.jpg,
|
||||
uiSettings: [
|
||||
IOSUiSettings(
|
||||
title: '头像剪裁',
|
||||
doneButtonTitle: '完成',
|
||||
cancelButtonTitle: '取消',
|
||||
),
|
||||
AndroidUiSettings(
|
||||
toolbarTitle: '头像剪裁',
|
||||
),
|
||||
],
|
||||
);
|
||||
if (croppedFile != null) {
|
||||
UserController.to.uploadAvatar(croppedFile.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
import 'package:chat/controllers/user_controller.dart';
|
||||
import 'package:chat/routes/user_routes.dart';
|
||||
import 'package:chat/services/auth_service.dart';
|
||||
import 'package:chat/views/user/widgets/link_action_item.dart';
|
||||
import 'package:chat/widgets/custom_avatar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||
|
||||
class UserInfoPage extends StatefulWidget {
|
||||
const UserInfoPage({Key? key}) : super(key: key);
|
||||
@@ -24,26 +21,12 @@ class _UserInfoPageState extends State<UserInfoPage> {
|
||||
),
|
||||
body: GetX<AuthService>(
|
||||
builder: (_) {
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
return Column(
|
||||
children: [
|
||||
LinkActionItem(
|
||||
title: '头像',
|
||||
onTap: () async {
|
||||
final result = await AssetPicker.pickAssets(
|
||||
Get.context!,
|
||||
pickerConfig: const AssetPickerConfig(
|
||||
maxAssets: 1,
|
||||
textDelegate: AssetPickerTextDelegate(),
|
||||
requestType: RequestType.image,
|
||||
),
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
_cropImage((await result.first.file)!.path);
|
||||
Get.toNamed(UserRoutes.infoAvatar);
|
||||
},
|
||||
isLink: true,
|
||||
trailing: CustomAvatar(
|
||||
@@ -64,39 +47,10 @@ class _UserInfoPageState extends State<UserInfoPage> {
|
||||
);
|
||||
},
|
||||
),
|
||||
Expanded(child: Container()),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _cropImage(String imagePath) async {
|
||||
CroppedFile? croppedFile = await ImageCropper().cropImage(
|
||||
sourcePath: imagePath,
|
||||
maxHeight: 128,
|
||||
maxWidth: 128,
|
||||
compressQuality: 100,
|
||||
aspectRatio: const CropAspectRatio(
|
||||
ratioX: 1,
|
||||
ratioY: 1,
|
||||
),
|
||||
compressFormat: ImageCompressFormat.jpg,
|
||||
uiSettings: [
|
||||
IOSUiSettings(
|
||||
title: '头像剪裁',
|
||||
doneButtonTitle: '完成',
|
||||
cancelButtonTitle: '取消',
|
||||
),
|
||||
AndroidUiSettings(
|
||||
toolbarTitle: '头像剪裁',
|
||||
),
|
||||
],
|
||||
);
|
||||
if (croppedFile != null) {
|
||||
UserController.to.uploadAvatar(croppedFile.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:chat/services/auth_service.dart';
|
||||
import 'package:chat/views/home/widgets/pop_menu_item.dart';
|
||||
import 'package:chat/views/public/scan_page.dart';
|
||||
import 'package:chat/views/public/scan/index_page.dart';
|
||||
import 'package:chat/widgets/custom_avatar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -43,34 +44,28 @@ class _UserQrCodePageState extends State<UserQrCodePage> {
|
||||
Row(
|
||||
children: [
|
||||
CustomAvatar(
|
||||
'',
|
||||
AuthService.to.userInfo.value.avatar,
|
||||
size: 64,
|
||||
radius: 6,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: const [
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Jason',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
AuthService.to.userInfo.value.nickname ?? '',
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
QrImage(
|
||||
data: 'BEFRIEND|5',
|
||||
data: 'BEFRIEND|${AuthService.to.userInfo.value.username}',
|
||||
version: 3,
|
||||
size: Get.width * 0.8,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
const Text(
|
||||
'扫一扫上面的二维码,加我共力好友',
|
||||
'扫一扫上面的二维码,加我ZH-CHAT好友',
|
||||
style: TextStyle(
|
||||
color: AppColors.unactive,
|
||||
),
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UserSafePage extends StatefulWidget {
|
||||
const UserSafePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<UserSafePage> createState() => _UserSafePageState();
|
||||
}
|
||||
|
||||
class _UserSafePageState extends State<UserSafePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('安全设置'),
|
||||
),
|
||||
body: Container(),
|
||||
);
|
||||
}
|
||||
}
|
||||
60
lib/views/user/serve/google/index_page.dart
Normal file
60
lib/views/user/serve/google/index_page.dart
Normal file
@@ -0,0 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:otp/otp.dart';
|
||||
|
||||
class UserServeGooglePage extends StatefulWidget {
|
||||
const UserServeGooglePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<UserServeGooglePage> createState() => _UserServeGooglePageState();
|
||||
}
|
||||
|
||||
class _UserServeGooglePageState extends State<UserServeGooglePage> {
|
||||
String code = '000000';
|
||||
String secret = 'T4UM3VPYXPALF7M5';
|
||||
int remaining = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
getCode();
|
||||
}
|
||||
|
||||
void getCode() {
|
||||
setState(() {
|
||||
remaining = OTP.remainingSeconds();
|
||||
|
||||
code = OTP.generateTOTPCodeString(
|
||||
secret,
|
||||
DateTime.now().millisecondsSinceEpoch,
|
||||
algorithm: Algorithm.SHA1,
|
||||
isGoogle: true,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('谷歌验证器'),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Text(
|
||||
code,
|
||||
style: const TextStyle(
|
||||
fontSize: 32,
|
||||
),
|
||||
),
|
||||
Text(remaining.toString()),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
getCode();
|
||||
},
|
||||
child: const Text('刷新密码'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
import 'package:chat/routes/app_routes.dart';
|
||||
import 'package:chat/views/home/widgets/action_item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class UserServePage extends StatefulWidget {
|
||||
const UserServePage({Key? key}) : super(key: key);
|
||||
@@ -14,7 +17,25 @@ class _UserServePageState extends State<UserServePage> {
|
||||
appBar: AppBar(
|
||||
title: const Text('服务'),
|
||||
),
|
||||
body: Container(),
|
||||
body: Column(
|
||||
children: [
|
||||
// ActionItem(
|
||||
// '谷歌验证器',
|
||||
// isFirst: true,
|
||||
// onTap: () {
|
||||
// Get.toNamed(UserRoutes.serveGoogle);
|
||||
// },
|
||||
// ),
|
||||
ActionItem(
|
||||
'扫一扫',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
Get.toNamed(AppRoutes.scan);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
20
lib/views/user/setting/about/index_page.dart
Normal file
20
lib/views/user/setting/about/index_page.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UserSettingAboutPage extends StatefulWidget {
|
||||
const UserSettingAboutPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_UserSettingAboutPageState createState() => _UserSettingAboutPageState();
|
||||
}
|
||||
|
||||
class _UserSettingAboutPageState extends State<UserSettingAboutPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('关于ZH-CHAT'),
|
||||
),
|
||||
body: Container(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:chat/providers/public_provider.dart';
|
||||
import 'package:chat/routes/user_routes.dart';
|
||||
import 'package:chat/services/auth_service.dart';
|
||||
import 'package:chat/views/home/widgets/action_button.dart';
|
||||
import 'package:chat/views/home/widgets/action_item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
class UserSettingPage extends StatefulWidget {
|
||||
const UserSettingPage({Key? key}) : super(key: key);
|
||||
@@ -14,6 +17,18 @@ class UserSettingPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _UserSettingPageState extends State<UserSettingPage> {
|
||||
PackageInfo? packageInfo;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
PackageInfo.fromPlatform().then((value) {
|
||||
setState(() {
|
||||
packageInfo = value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -24,89 +39,72 @@ class _UserSettingPageState extends State<UserSettingPage> {
|
||||
children: [
|
||||
ActionItem(
|
||||
'账号与安全',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.safe);
|
||||
Get.toNamed(UserRoutes.settingSafe);
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
ActionItem(
|
||||
'新消息通知',
|
||||
isFirst: true,
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.safe);
|
||||
Get.toNamed(UserRoutes.settingMessage);
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
indent: 16,
|
||||
),
|
||||
ActionItem(
|
||||
'隐私权限',
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.safe);
|
||||
Get.toNamed(UserRoutes.settingPrivacy);
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ActionItem(
|
||||
'关于ZH-CHAT',
|
||||
isFirst: true,
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.safe);
|
||||
Get.toNamed(UserRoutes.settingAbout);
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
indent: 16,
|
||||
),
|
||||
ActionItem(
|
||||
'帮助与反馈',
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.safe);
|
||||
Get.toNamed(UserRoutes.settingSugguest);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
ActionItem(
|
||||
'版本更新',
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.safe);
|
||||
extend: packageInfo?.version,
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
onTap: () async {
|
||||
PublicProvider.checkUpgrade(packageInfo);
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
ActionButton(
|
||||
'退出',
|
||||
color: AppColors.primary,
|
||||
hasTop: true,
|
||||
hasBottom: true,
|
||||
color: AppColors.red,
|
||||
onTap: () async {
|
||||
OkCancelResult result = await showOkCancelAlertDialog(
|
||||
style: AdaptiveStyle.iOS,
|
||||
context: context,
|
||||
title: '退出登录',
|
||||
message: '确认您已备份助记词并保存好了么?退出登录后助记词将无法导出。',
|
||||
okLabel: '确定',
|
||||
cancelLabel: '取消',
|
||||
defaultType: OkCancelAlertDefaultType.cancel,
|
||||
);
|
||||
if (result == OkCancelResult.ok) {
|
||||
AuthService.to.logout();
|
||||
}
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
20
lib/views/user/setting/message/index_page.dart
Normal file
20
lib/views/user/setting/message/index_page.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UserSettingMessagePage extends StatefulWidget {
|
||||
const UserSettingMessagePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_UserSettingMessagePageState createState() => _UserSettingMessagePageState();
|
||||
}
|
||||
|
||||
class _UserSettingMessagePageState extends State<UserSettingMessagePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('新消息与通知'),
|
||||
),
|
||||
body: Container(),
|
||||
);
|
||||
}
|
||||
}
|
||||
40
lib/views/user/setting/privacy/index_page.dart
Normal file
40
lib/views/user/setting/privacy/index_page.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:chat/services/auth_service.dart';
|
||||
import 'package:chat/views/user/widgets/link_action_item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get_state_manager/get_state_manager.dart';
|
||||
|
||||
class UserSettingPrivacyPage extends StatelessWidget {
|
||||
const UserSettingPrivacyPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('隐私权限'),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
const Divider(
|
||||
height: 0,
|
||||
color: AppColors.border,
|
||||
),
|
||||
GetX<AuthService>(
|
||||
builder: (_) {
|
||||
return LinkActionItem(
|
||||
title: '允许通过搜索添加我为好友',
|
||||
trailing: Switch(
|
||||
value: _.userInfo.value.privacy ?? true,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onChanged: (e) {
|
||||
_.togglePrivacy();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
15
lib/views/user/setting/safe/email_page.dart
Normal file
15
lib/views/user/setting/safe/email_page.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UserSettingSafeEmailPage extends StatelessWidget {
|
||||
const UserSettingSafeEmailPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('绑定邮箱'),
|
||||
),
|
||||
body: Container(),
|
||||
);
|
||||
}
|
||||
}
|
||||
15
lib/views/user/setting/safe/google_page.dart
Normal file
15
lib/views/user/setting/safe/google_page.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UserSettingSafeGooglePage extends StatelessWidget {
|
||||
const UserSettingSafeGooglePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('UserSettingSafeGooglePage'),
|
||||
),
|
||||
body: Container(),
|
||||
);
|
||||
}
|
||||
}
|
||||
110
lib/views/user/setting/safe/index_page.dart
Normal file
110
lib/views/user/setting/safe/index_page.dart
Normal file
@@ -0,0 +1,110 @@
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:chat/routes/user_routes.dart';
|
||||
import 'package:chat/services/auth_service.dart';
|
||||
import 'package:chat/utils/ui_tools.dart';
|
||||
import 'package:chat/views/home/widgets/action_item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class UserSettingSafePage extends StatefulWidget {
|
||||
const UserSettingSafePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<UserSettingSafePage> createState() => _UserSettingSafePageState();
|
||||
}
|
||||
|
||||
class _UserSettingSafePageState extends State<UserSettingSafePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('安全设置'),
|
||||
),
|
||||
body: GetX<AuthService>(builder: (_) {
|
||||
return Column(
|
||||
children: [
|
||||
ActionItem(
|
||||
'导出助记词',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
onTap: _showMnemonic,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ActionItem(
|
||||
'绑定手机',
|
||||
extend: _.userInfo.value.mobile ?? '未绑定',
|
||||
isFirst: true,
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.settingSafeMobile);
|
||||
},
|
||||
),
|
||||
ActionItem(
|
||||
'绑定邮箱',
|
||||
extend: _.userInfo.value.email ?? '未绑定',
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.settingSafeEmail);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ActionItem(
|
||||
'开启两步验证',
|
||||
extend: (_.userInfo.value.google2fa ?? false) ? '已开启' : '未开启',
|
||||
isFirst: true,
|
||||
isLast: true,
|
||||
onTap: () {
|
||||
Get.toNamed(UserRoutes.settingSafeGoogle);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
void _showMnemonic() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: AppColors.white,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
|
||||
),
|
||||
builder: (context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
const Text('您的助记词'),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Text(
|
||||
AuthService.to.mnemonic,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
wordSpacing: 6,
|
||||
height: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: AuthService.to.mnemonic,
|
||||
),
|
||||
);
|
||||
UiTools.toast('复制成功');
|
||||
Get.back();
|
||||
},
|
||||
child: const Text('复制'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
17
lib/views/user/setting/safe/mobile_page.dart
Normal file
17
lib/views/user/setting/safe/mobile_page.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UserSettingSafeMobilePage extends StatelessWidget {
|
||||
const UserSettingSafeMobilePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('绑定手机'),
|
||||
),
|
||||
body: Column(
|
||||
children: const [],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
21
lib/views/user/setting/sugguest/index_page.dart
Normal file
21
lib/views/user/setting/sugguest/index_page.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UserSettingSugguestPage extends StatefulWidget {
|
||||
const UserSettingSugguestPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_UserSettingSugguestPageState createState() =>
|
||||
_UserSettingSugguestPageState();
|
||||
}
|
||||
|
||||
class _UserSettingSugguestPageState extends State<UserSettingSugguestPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('帮助与反馈'),
|
||||
),
|
||||
body: Container(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
import 'package:chat/configs/app_colors.dart';
|
||||
import 'package:chat/providers/user_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
class UserSharePage extends StatefulWidget {
|
||||
const UserSharePage({Key? key}) : super(key: key);
|
||||
@@ -14,7 +18,33 @@ class _UserSharePageState extends State<UserSharePage> {
|
||||
appBar: AppBar(
|
||||
title: const Text('分享邀请'),
|
||||
),
|
||||
body: Container(),
|
||||
body: FutureBuilder(
|
||||
future: UserProvider.downloadUrl(),
|
||||
builder: (context, AsyncSnapshot<String?> data) {
|
||||
return data.data != null
|
||||
? Center(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
QrImage(
|
||||
data: '${data.data}',
|
||||
size: Get.width * 0.8,
|
||||
),
|
||||
const Text('扫一扫上面的二维码,下载ZH-CHAT'),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: Container();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ class CustomEasyRefresh {
|
||||
double size = 156.0,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
@@ -43,6 +42,9 @@ class CustomEasyRefresh {
|
||||
'assets/images/empty/im_emptyIcon_2.png',
|
||||
width: size,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
|
||||
91
pubspec.lock
91
pubspec.lock
@@ -28,7 +28,7 @@ packages:
|
||||
name: asn1lib
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -43,6 +43,13 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
base32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: base32
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
bip32:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -121,7 +128,7 @@ packages:
|
||||
name: camera_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
version: "2.3.0"
|
||||
camera_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -206,13 +213,6 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
device_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.1"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -336,7 +336,7 @@ packages:
|
||||
name: flutter_plugin_record_plus
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.11"
|
||||
version: "0.0.15"
|
||||
flutter_spinkit:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -437,21 +437,21 @@ packages:
|
||||
name: image_cropper
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "3.0.1"
|
||||
image_cropper_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_cropper_for_web
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "1.0.3"
|
||||
image_cropper_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_cropper_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.0.3"
|
||||
intersperse:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -487,6 +487,13 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
lpinyin:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -543,13 +550,55 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
otp:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: otp
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
package_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.1"
|
||||
version: "1.4.2"
|
||||
package_info_plus_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_linux
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
package_info_plus_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_macos
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
package_info_plus_web:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus_web
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
package_info_plus_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_windows
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -570,7 +619,7 @@ packages:
|
||||
name: path_provider_android
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.20"
|
||||
version: "2.0.21"
|
||||
path_provider_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -738,7 +787,7 @@ packages:
|
||||
name: rxdart
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.27.5"
|
||||
version: "0.27.6"
|
||||
scan:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -792,7 +841,7 @@ packages:
|
||||
name: sqflite_common
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.4.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -813,7 +862,7 @@ packages:
|
||||
name: stream_transform
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.1.0"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -834,14 +883,14 @@ packages:
|
||||
name: tencent_im_sdk_plugin
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.1.9"
|
||||
version: "4.2.0"
|
||||
tencent_im_sdk_plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: tencent_im_sdk_plugin_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.3.5"
|
||||
version: "0.3.6"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -883,7 +932,7 @@ packages:
|
||||
name: uuid
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
version: "3.0.7"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -16,8 +16,6 @@ dependencies:
|
||||
lpinyin: ^2.0.3
|
||||
vibration: ^1.7.6
|
||||
scan: ^1.6.0
|
||||
package_info_plus: ^0.0.1
|
||||
device_info_plus: ^0.0.1
|
||||
flutter_easyrefresh: ^2.2.2
|
||||
cached_network_image: ^3.2.0
|
||||
flutter_spinkit: ^5.1.0
|
||||
@@ -52,6 +50,9 @@ dependencies:
|
||||
wechat_camera_picker: ^3.5.0+1
|
||||
filesize: ^2.0.1
|
||||
file_picker: ^4.6.1
|
||||
otp: ^3.1.1
|
||||
package_info_plus: ^1.4.2
|
||||
package_info_plus_web: ^1.0.6
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user