Browse Source

fate : 监听联网状态

dev
徐振升 2 weeks ago
parent
commit
34447b0826
  1. 3
      lib/app/bindings/initial_binding.dart
  2. 62
      lib/data/providers/connectivity_provider.dart
  3. 6
      lib/modules/auth/bindings/auth_binding.dart
  4. 118
      lib/modules/auth/controllers/auth_controller.dart
  5. 53
      lib/modules/auth/views/login_page.dart
  6. 15
      lib/modules/home/bindings/home_binding.dart
  7. 5
      lib/modules/my/views/my_page.dart
  8. 2
      lib/modules/problem/bindings/problem_binding.dart
  9. 43
      lib/modules/problem/controllers/problem_controller.dart
  10. 52
      lib/modules/problem/views/problem_page.dart
  11. 2
      pubspec.lock
  12. 1
      pubspec.yaml

3
lib/app/bindings/initial_binding.dart

@ -2,6 +2,7 @@ import 'package:get/get.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart'; // debugPrint import 'package:flutter/foundation.dart'; // debugPrint
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart';
import 'package:problem_check_system/data/providers/local_database.dart'; import 'package:problem_check_system/data/providers/local_database.dart';
class InitialBinding implements Bindings { class InitialBinding implements Bindings {
@ -9,11 +10,11 @@ class InitialBinding implements Bindings {
void dependencies() { void dependencies() {
// GetStorage - 使 put // GetStorage - 使 put
Get.put<GetStorage>(GetStorage(), permanent: true); Get.put<GetStorage>(GetStorage(), permanent: true);
// Dio - 使 put lazyPut // Dio - 使 put lazyPut
Get.put<Dio>(createDioInstance()); Get.put<Dio>(createDioInstance());
// //
Get.put<LocalDatabase>(LocalDatabase()); Get.put<LocalDatabase>(LocalDatabase());
Get.put<ConnectivityProvider>(ConnectivityProvider());
} }
// Dio // Dio

62
lib/data/providers/connectivity_provider.dart

@ -0,0 +1,62 @@
// lib/data/providers/connectivity_provider.dart
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
class ConnectivityProvider extends GetxService {
final Connectivity _connectivity = Connectivity();
final RxBool isOnline = false.obs;
late StreamSubscription<List<ConnectivityResult>> _connectivitySubscription;
@override
void onInit() {
super.onInit();
_initConnectivityListener();
}
@override
void onClose() {
_connectivitySubscription.cancel();
super.onClose();
}
Future<void> _initConnectivityListener() async {
_connectivitySubscription = _connectivity.onConnectivityChanged.listen((
results,
) {
final isConnected = results.any(
(result) =>
result == ConnectivityResult.mobile ||
result == ConnectivityResult.wifi ||
result == ConnectivityResult.ethernet,
);
isOnline.value = isConnected;
if (isConnected) {
Get.snackbar(
'网络状态',
'已连接到网络',
colorText: Colors.white,
backgroundColor: Colors.green,
snackPosition: SnackPosition.TOP,
);
} else {
Get.snackbar(
'网络状态',
'已断开网络连接',
colorText: Colors.white,
backgroundColor: Colors.red,
snackPosition: SnackPosition.TOP,
);
}
});
final initialResults = await _connectivity.checkConnectivity();
isOnline.value = initialResults.any(
(result) =>
result == ConnectivityResult.mobile ||
result == ConnectivityResult.wifi ||
result == ConnectivityResult.ethernet,
);
}
}

6
lib/modules/auth/bindings/auth_binding.dart

@ -1,5 +1,6 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:get_storage/get_storage.dart';
import 'package:problem_check_system/modules/auth/controllers/auth_controller.dart'; import 'package:problem_check_system/modules/auth/controllers/auth_controller.dart';
import 'package:problem_check_system/data/providers/auth_provider.dart'; import 'package:problem_check_system/data/providers/auth_provider.dart';
@ -13,7 +14,10 @@ class AuthBinding implements Bindings {
// 2. (AuthController) AuthProvider // 2. (AuthController) AuthProvider
// Get.find() // Get.find()
Get.lazyPut<AuthController>( Get.lazyPut<AuthController>(
() => AuthController(authProvider: Get.find<AuthProvider>()), () => AuthController(
authProvider: Get.find<AuthProvider>(),
storage: Get.find<GetStorage>(),
),
); );
} }
} }

118
lib/modules/auth/controllers/auth_controller.dart

@ -1,68 +1,99 @@
// lib/modules/auth/controllers/auth_controller.dart
import 'package:flutter/material.dart'; // 使 TextEditingController
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
//
import 'package:problem_check_system/data/providers/auth_provider.dart'; import 'package:problem_check_system/data/providers/auth_provider.dart';
import 'package:problem_check_system/data/models/login_model.dart'; import 'package:problem_check_system/data/models/login_model.dart';
import 'package:problem_check_system/app/routes/app_routes.dart'; import 'package:problem_check_system/app/routes/app_routes.dart';
class AuthController extends GetxController { class AuthController extends GetxController {
final AuthProvider _authProvider; final AuthProvider _authProvider;
AuthController({required AuthProvider authProvider}) final GetStorage _storage;
: _authProvider = authProvider;
// TextEditingController
late final TextEditingController usernameController;
late final TextEditingController passwordController;
final username = ''.obs;
final password = ''.obs;
final isLoading = false.obs; final isLoading = false.obs;
final rememberPassword = false.obs; final rememberPassword = false.obs;
final _box = GetStorage();
static const _usernameKey = 'username'; static const _usernameKey = 'username';
static const _passwordKey = 'password'; static const _passwordKey = 'password';
AuthController({
required AuthProvider authProvider,
required GetStorage storage,
}) : _authProvider = authProvider,
_storage = storage;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// onInit
usernameController = TextEditingController();
passwordController = TextEditingController();
_loadSavedCredentials(); _loadSavedCredentials();
} }
@override
void onClose() {
// onClose
usernameController.dispose();
passwordController.dispose();
super.onClose();
}
void _loadSavedCredentials() { void _loadSavedCredentials() {
final savedUsername = _box.read(_usernameKey); final savedUsername = _storage.read(_usernameKey);
final savedPassword = _box.read(_passwordKey); final savedPassword = _storage.read(_passwordKey);
if (savedUsername != null && savedPassword != null) { if (savedUsername != null && savedPassword != null) {
username.value = savedUsername; //
password.value = savedPassword; usernameController.text = savedUsername;
passwordController.text = savedPassword;
rememberPassword.value = true; rememberPassword.value = true;
} }
} }
String getToken() { String getToken() {
return ''; return _storage.read('token') ?? '';
} }
void updateUsername(String value) { // updateUsername updatePassword
username.value = value; // TextEditingController
}
void updatePassword(String value) {
password.value = value;
}
Future<void> login() async { Future<void> login() async {
if (username.value.isEmpty || password.value.isEmpty) { //
final username = usernameController.text;
final password = passwordController.text;
if (username.isEmpty || password.isEmpty) {
Get.snackbar('输入错误', '用户名和密码不能为空'); Get.snackbar('输入错误', '用户名和密码不能为空');
return; return;
} }
isLoading.value = true; isLoading.value = true;
var connectivityResult = await (Connectivity().checkConnectivity());
bool isConnected =
connectivityResult.contains(ConnectivityResult.mobile) ||
connectivityResult.contains(ConnectivityResult.wifi) ||
connectivityResult.contains(ConnectivityResult.ethernet);
if (isConnected) {
await _onlineLogin(username, password);
} else {
await _offlineLogin(username, password);
}
isLoading.value = false;
}
Future<void> _onlineLogin(String username, String password) async {
try { try {
final loginData = LoginModel( final loginData = LoginModel(username: username, password: password);
username: username.value,
password: password.value,
);
final response = await _authProvider.signIn(loginData); final response = await _authProvider.signIn(loginData);
@ -70,15 +101,17 @@ class AuthController extends GetxController {
final token = response.data['token']; final token = response.data['token'];
final refreshToken = response.data['refreshToken']; final refreshToken = response.data['refreshToken'];
_box.write('token', token); _storage.write('token', token);
_box.write('refreshToken', refreshToken); _storage.write('refreshToken', refreshToken);
//
if (rememberPassword.value) { if (rememberPassword.value) {
_box.write(_usernameKey, username.value); _storage.write(_usernameKey, username);
_box.write(_passwordKey, password.value); _storage.write(_passwordKey, password);
} else { } else {
_box.remove(_usernameKey); //
_box.remove(_passwordKey); _storage.remove(_usernameKey);
_storage.remove(_passwordKey);
} }
Get.offAllNamed(AppRoutes.home); Get.offAllNamed(AppRoutes.home);
@ -94,18 +127,27 @@ class AuthController extends GetxController {
} }
} catch (e) { } catch (e) {
Get.snackbar('错误', '发生未知错误: ${e.toString()}'); Get.snackbar('错误', '发生未知错误: ${e.toString()}');
} finally {
isLoading.value = false;
} }
} }
Future<void> logout() async { Future<void> _offlineLogin(String username, String password) async {
_box.remove('token'); final storedUsername = _storage.read(_usernameKey);
_box.remove('refreshToken'); final storedPassword = _storage.read(_passwordKey);
if (rememberPassword.value == false) {
_box.remove(_usernameKey); if (storedUsername != null &&
_box.remove(_passwordKey); storedPassword != null &&
storedUsername == username &&
storedPassword == password) {
Get.offAllNamed(AppRoutes.home);
Get.snackbar('离线登录成功', '您已离线登录到系统');
} else {
Get.snackbar('离线登录失败', '无网络连接,且无法验证本地凭证');
} }
}
Future<void> logout() async {
_storage.remove('token');
_storage.remove('refreshToken');
Get.offAllNamed(AppRoutes.login); Get.offAllNamed(AppRoutes.login);
} }
} }

53
lib/modules/auth/views/login_page.dart

@ -1,43 +1,20 @@
// lib/modules/auth/views/login_page.dart
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:problem_check_system/modules/auth/controllers/auth_controller.dart'; import 'package:problem_check_system/modules/auth/controllers/auth_controller.dart';
class LoginPage extends StatelessWidget { class LoginPage extends GetView<AuthController> {
const LoginPage({super.key}); const LoginPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final AuthController controller = Get.find<AuthController>();
// build TextEditingController
final TextEditingController usernameController = TextEditingController(
text: controller.username.value,
);
final TextEditingController passwordController = TextEditingController(
text: controller.password.value,
);
// AuthController
// TextEditingController
// 使 once() ever()
// onChanged
usernameController.text = controller.username.value;
passwordController.text = controller.password.value;
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: false, body: Stack(children: [_buildBackground(), _buildLoginCard(controller)]),
body: Stack(
children: [
_buildBackground(),
_buildLoginCard(controller, usernameController, passwordController),
],
),
); );
} }
Widget _buildBackground() { Widget _buildBackground() {
// ... ...
return Stack( return Stack(
children: [ children: [
Container( Container(
@ -72,12 +49,8 @@ class LoginPage extends StatelessWidget {
); );
} }
// _buildLoginCard // _buildLoginCard TextEditingController
Widget _buildLoginCard( Widget _buildLoginCard(AuthController controller) {
AuthController controller,
TextEditingController usernameController,
TextEditingController passwordController,
) {
return Positioned( return Positioned(
left: 20.5.w, left: 20.5.w,
top: 220.5.h, top: 220.5.h,
@ -85,7 +58,7 @@ class LoginPage extends StatelessWidget {
width: 334.w, width: 334.w,
height: 574.5.h, height: 574.5.h,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFFFFFFF).withOpacity(0.6), // 使 withOpacity color: const Color(0xFFFFFFFF).withOpacity(0.6),
borderRadius: BorderRadius.all(Radius.circular(23.5.r)), borderRadius: BorderRadius.all(Radius.circular(23.5.r)),
), ),
padding: EdgeInsets.all(24.w), padding: EdgeInsets.all(24.w),
@ -93,20 +66,18 @@ class LoginPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(height: 16), const SizedBox(height: 16),
// 使 _buildTextFieldSection // 使 TextEditingController
_buildTextFieldSection( _buildTextFieldSection(
label: '账号', label: '账号',
hintText: '请输入您的账号', hintText: '请输入您的账号',
controller: usernameController, controller: controller.usernameController,
onChanged: controller.updateUsername, // onChanged
), ),
const SizedBox(height: 22), const SizedBox(height: 22),
_buildTextFieldSection( _buildTextFieldSection(
label: '密码', label: '密码',
hintText: '请输入您的密码', hintText: '请输入您的密码',
obscureText: true, obscureText: true,
controller: passwordController, controller: controller.passwordController,
onChanged: controller.updatePassword,
), ),
const SizedBox(height: 9.5), const SizedBox(height: 9.5),
_buildRememberPasswordRow(controller), _buildRememberPasswordRow(controller),
@ -118,13 +89,12 @@ class LoginPage extends StatelessWidget {
); );
} }
// _buildTextFieldSection TextEditingController // _buildTextFieldSection onChanged
Widget _buildTextFieldSection({ Widget _buildTextFieldSection({
required String label, required String label,
required String hintText, required String hintText,
required TextEditingController controller, required TextEditingController controller,
bool obscureText = false, bool obscureText = false,
required Function(String) onChanged,
}) { }) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -136,7 +106,6 @@ class LoginPage extends StatelessWidget {
const SizedBox(height: 10.5), const SizedBox(height: 10.5),
TextField( TextField(
controller: controller, // 使 controller: controller, // 使
onChanged: onChanged,
obscureText: obscureText, obscureText: obscureText,
style: const TextStyle(color: Colors.black), style: const TextStyle(color: Colors.black),
decoration: InputDecoration( decoration: InputDecoration(
@ -150,7 +119,6 @@ class LoginPage extends StatelessWidget {
} }
Widget _buildRememberPasswordRow(AuthController controller) { Widget _buildRememberPasswordRow(AuthController controller) {
// ... ...
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
@ -169,7 +137,6 @@ class LoginPage extends StatelessWidget {
} }
Widget _buildLoginButton(AuthController controller) { Widget _buildLoginButton(AuthController controller) {
// ... ...
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
child: ElevatedButton( child: ElevatedButton(

15
lib/modules/home/bindings/home_binding.dart

@ -1,6 +1,8 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:problem_check_system/data/providers/auth_provider.dart'; import 'package:problem_check_system/data/providers/auth_provider.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart';
import 'package:problem_check_system/data/providers/local_database.dart'; import 'package:problem_check_system/data/providers/local_database.dart';
import 'package:problem_check_system/modules/auth/controllers/auth_controller.dart'; import 'package:problem_check_system/modules/auth/controllers/auth_controller.dart';
import 'package:problem_check_system/modules/home/controllers/home_controller.dart'; import 'package:problem_check_system/modules/home/controllers/home_controller.dart';
@ -12,15 +14,24 @@ class HomeBinding implements Bindings {
void dependencies() { void dependencies() {
final Dio dio = Get.find<Dio>(); final Dio dio = Get.find<Dio>();
final LocalDatabase database = Get.find<LocalDatabase>(); final LocalDatabase database = Get.find<LocalDatabase>();
final ConnectivityProvider connectivityProvider =
Get.find<ConnectivityProvider>();
// HomeController // HomeController
Get.lazyPut<HomeController>(() => HomeController()); Get.lazyPut<HomeController>(() => HomeController());
Get.lazyPut<ProblemController>( Get.lazyPut<ProblemController>(
() => ProblemController(localDatabase: database, dio: dio), () => ProblemController(
localDatabase: database,
dio: dio,
connectivityProvider: connectivityProvider,
),
); );
Get.lazyPut<MyController>(() => MyController()); Get.lazyPut<MyController>(() => MyController());
Get.lazyPut<AuthProvider>(() => AuthProvider(dio: dio)); Get.lazyPut<AuthProvider>(() => AuthProvider(dio: dio));
Get.lazyPut<AuthController>( Get.lazyPut<AuthController>(
() => AuthController(authProvider: Get.find<AuthProvider>()), () => AuthController(
authProvider: Get.find<AuthProvider>(),
storage: Get.find<GetStorage>(),
),
); );
} }
} }

5
lib/modules/my/views/my_page.dart

@ -40,10 +40,7 @@ class MyPage extends StatelessWidget {
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [const Color(0xFF418CFC), const Color(0x713DBFFC)],
const Color(0xFFE4F0FF),
const Color(0xFFF1F7FF).withValues(alpha: 25.5),
],
), ),
), ),
), ),

2
lib/modules/problem/bindings/problem_binding.dart

@ -1,5 +1,6 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:problem_check_system/data/providers/connectivity_provider.dart';
import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart'; import 'package:problem_check_system/modules/problem/controllers/problem_controller.dart';
import 'package:problem_check_system/data/providers/local_database.dart'; import 'package:problem_check_system/data/providers/local_database.dart';
@ -13,6 +14,7 @@ class ProblemBinding implements Bindings {
() => ProblemController( () => ProblemController(
localDatabase: Get.find<LocalDatabase>(), localDatabase: Get.find<LocalDatabase>(),
dio: Get.find<Dio>(), dio: Get.find<Dio>(),
connectivityProvider: Get.find<ConnectivityProvider>(),
), ),
); );
} }

43
lib/modules/problem/controllers/problem_controller.dart

@ -1,27 +1,34 @@
// modules/problem/controllers/problem_controller.dart
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:get/get.dart' hide MultipartFile, FormData; import 'package:get/get.dart' hide MultipartFile, FormData;
import 'dart:io'; import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../../../data/models/problem_model.dart'; import '../../../data/models/problem_model.dart';
import '../../../data/providers/local_database.dart'; import '../../../data/providers/local_database.dart';
import '../../../data/providers/connectivity_provider.dart'; //
class ProblemController extends GetxController { class ProblemController extends GetxController {
final LocalDatabase _localDatabase; final LocalDatabase _localDatabase;
final RxList<Problem> problems = <Problem>[].obs; final RxList<Problem> problems = <Problem>[].obs;
final RxBool isLoading = false.obs; final RxBool isLoading = false.obs;
final Dio _dio; final Dio _dio;
final ConnectivityProvider _connectivityProvider;
// bindings ProblemController({
ProblemController({required LocalDatabase localDatabase, required Dio dio}) required LocalDatabase localDatabase,
: _localDatabase = localDatabase, required Dio dio,
_dio = dio; required ConnectivityProvider connectivityProvider,
}) : _localDatabase = localDatabase,
_dio = dio,
_connectivityProvider = connectivityProvider;
// 使 provider isOnline
RxBool get isOnline => _connectivityProvider.isOnline;
//
List<Problem> get selectedProblems { List<Problem> get selectedProblems {
return problems.where((p) => p.isChecked.value).toList(); return problems.where((p) => p.isChecked.value).toList();
} }
//
List<Problem> get unuploadedProblems { List<Problem> get unuploadedProblems {
return problems.where((p) => !p.isUploaded).toList(); return problems.where((p) => !p.isUploaded).toList();
} }
@ -44,16 +51,13 @@ class ProblemController extends GetxController {
} }
} }
/// GetX列表中添加一个新问题
Future<void> addProblem(Problem problem) async { Future<void> addProblem(Problem problem) async {
try { try {
// ID
if (problem.id == null) { if (problem.id == null) {
problem = problem.copyWith( problem = problem.copyWith(
id: DateTime.now().millisecondsSinceEpoch.toString(), id: DateTime.now().millisecondsSinceEpoch.toString(),
); );
} }
await _localDatabase.insertProblem(problem); await _localDatabase.insertProblem(problem);
problems.add(problem); problems.add(problem);
} catch (e) { } catch (e) {
@ -62,7 +66,6 @@ class ProblemController extends GetxController {
} }
} }
/// GetX列表中更新一个现有问题
Future<void> updateProblem(Problem problem) async { Future<void> updateProblem(Problem problem) async {
try { try {
await _localDatabase.updateProblem(problem); await _localDatabase.updateProblem(problem);
@ -76,16 +79,11 @@ class ProblemController extends GetxController {
} }
} }
///
Future<void> deleteProblem(Problem problem) async { Future<void> deleteProblem(Problem problem) async {
try { try {
if (problem.id != null) { if (problem.id != null) {
await _localDatabase.deleteProblem(problem.id!); await _localDatabase.deleteProblem(problem.id!);
//
problems.remove(problem); problems.remove(problem);
//
await _deleteProblemImages(problem); await _deleteProblemImages(problem);
} }
} catch (e) { } catch (e) {
@ -94,14 +92,12 @@ class ProblemController extends GetxController {
} }
} }
///
Future<void> deleteSelectedProblems() async { Future<void> deleteSelectedProblems() async {
final problemsToDelete = selectedProblems; final problemsToDelete = selectedProblems;
if (problemsToDelete.isEmpty) { if (problemsToDelete.isEmpty) {
Get.snackbar('提示', '请至少选择一个问题进行删除'); Get.snackbar('提示', '请至少选择一个问题进行删除');
return; return;
} }
try { try {
for (var problem in problemsToDelete) { for (var problem in problemsToDelete) {
await deleteProblem(problem); await deleteProblem(problem);
@ -112,7 +108,6 @@ class ProblemController extends GetxController {
} }
} }
///
Future<void> _deleteProblemImages(Problem problem) async { Future<void> _deleteProblemImages(Problem problem) async {
for (var imagePath in problem.imagePaths) { for (var imagePath in problem.imagePaths) {
try { try {
@ -126,7 +121,6 @@ class ProblemController extends GetxController {
} }
} }
///
Future<bool> uploadProblem(Problem problem) async { Future<bool> uploadProblem(Problem problem) async {
try { try {
final formData = FormData.fromMap({ final formData = FormData.fromMap({
@ -136,7 +130,6 @@ class ProblemController extends GetxController {
'boundInfo': problem.boundInfo ?? '', 'boundInfo': problem.boundInfo ?? '',
}); });
//
for (var imagePath in problem.imagePaths) { for (var imagePath in problem.imagePaths) {
final file = File(imagePath); final file = File(imagePath);
if (await file.exists()) { if (await file.exists()) {
@ -162,7 +155,6 @@ class ProblemController extends GetxController {
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
//
final updatedProblem = problem.copyWith(isUploaded: true); final updatedProblem = problem.copyWith(isUploaded: true);
await updateProblem(updatedProblem); await updateProblem(updatedProblem);
return true; return true;
@ -183,8 +175,12 @@ class ProblemController extends GetxController {
} }
} }
///
Future<void> uploadAllUnuploaded() async { Future<void> uploadAllUnuploaded() async {
if (!isOnline.value) {
Get.snackbar('提示', '当前无网络,无法上传');
return;
}
final unuploaded = unuploadedProblems; final unuploaded = unuploadedProblems;
if (unuploaded.isEmpty) { if (unuploaded.isEmpty) {
Get.snackbar('提示', '没有需要上传的问题'); Get.snackbar('提示', '没有需要上传的问题');
@ -199,7 +195,6 @@ class ProblemController extends GetxController {
if (success) { if (success) {
successCount++; successCount++;
} }
//
await Future.delayed(const Duration(milliseconds: 500)); await Future.delayed(const Duration(milliseconds: 500));
} }
@ -214,7 +209,6 @@ class ProblemController extends GetxController {
} }
} }
///
Future<void> bindInfoToProblem(String id, String info) async { Future<void> bindInfoToProblem(String id, String info) async {
try { try {
final problem = problems.firstWhere((p) => p.id == id); final problem = problems.firstWhere((p) => p.id == id);
@ -226,7 +220,6 @@ class ProblemController extends GetxController {
} }
} }
///
void clearSelections() { void clearSelections() {
for (var problem in problems) { for (var problem in problems) {
problem.isChecked.value = false; problem.isChecked.value = false;

52
lib/modules/problem/views/problem_page.dart

@ -1,3 +1,4 @@
// problem_page.dart
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -28,12 +29,9 @@ class ProblemPage extends StatelessWidget {
alignment: Alignment.bottomLeft, alignment: Alignment.bottomLeft,
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.centerLeft, // begin: Alignment.centerLeft,
end: Alignment.centerRight, // end: Alignment.centerRight,
colors: [ colors: [Color(0xFF418CFC), Color(0xFF3DBFFC)],
Color(0xFF418CFC), //
Color(0xFF3DBFFC), //
],
), ),
), ),
child: TabBar( child: TabBar(
@ -50,17 +48,17 @@ class ProblemPage extends StatelessWidget {
Tab(text: '历史问题列表'), Tab(text: '历史问题列表'),
], ],
labelStyle: TextStyle( labelStyle: TextStyle(
fontFamily: 'MyFont', // fontFamily: 'MyFont',
fontWeight: FontWeight.w800, // fontWeight: FontWeight.w800,
fontSize: 14.sp, // fontSize: 14.sp,
), ),
unselectedLabelStyle: TextStyle( unselectedLabelStyle: TextStyle(
fontFamily: 'MyFont', fontFamily: 'MyFont',
fontWeight: FontWeight.w800, fontWeight: FontWeight.w800,
fontSize: 14.sp, fontSize: 14.sp,
), ),
labelColor: Colors.black, // labelColor: Colors.black,
unselectedLabelColor: Colors.white, // unselectedLabelColor: Colors.white,
), ),
), ),
Expanded( Expanded(
@ -139,30 +137,32 @@ class ProblemPage extends StatelessWidget {
], ],
), ),
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: Obx(() {
final bool isOnline = problemController.isOnline.value;
return FloatingActionButton(
heroTag: "abc", heroTag: "abc",
onPressed: () { onPressed: isOnline
// ? () => problemController.uploadAllUnuploaded()
problemController.uploadAllUnuploaded(); : null,
},
foregroundColor: Colors.white, foregroundColor: Colors.white,
backgroundColor: Colors.red[300], backgroundColor: isOnline ? Colors.red[300] : Colors.grey[400],
child: const Icon(Icons.file_upload_outlined), child: Icon(
isOnline ? Icons.file_upload_outlined : Icons.cloud_off_outlined,
), ),
);
}),
), ),
); );
} }
// ProblemCard组件
Widget _buildSwipeableProblemCard( Widget _buildSwipeableProblemCard(
Problem problem, Problem problem,
ProblemController controller, { ProblemController controller, {
ProblemCardViewType viewType = ProblemCardViewType.buttons, ProblemCardViewType viewType = ProblemCardViewType.buttons,
}) { }) {
// 使Dismissible组件实现左滑删除
return Dismissible( return Dismissible(
key: Key(problem.id ?? UniqueKey().toString()), key: Key(problem.id ?? UniqueKey().toString()),
direction: DismissDirection.endToStart, // direction: DismissDirection.endToStart,
background: Container( background: Container(
color: Colors.red, color: Colors.red,
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
@ -170,11 +170,9 @@ class ProblemPage extends StatelessWidget {
child: Icon(Icons.delete, color: Colors.white, size: 30.sp), child: Icon(Icons.delete, color: Colors.white, size: 30.sp),
), ),
confirmDismiss: (direction) async { confirmDismiss: (direction) async {
//
return await _showDeleteConfirmationDialog(problem); return await _showDeleteConfirmationDialog(problem);
}, },
onDismissed: (direction) { onDismissed: (direction) {
//
controller.deleteProblem(problem); controller.deleteProblem(problem);
Get.snackbar('成功', '问题已删除'); Get.snackbar('成功', '问题已删除');
}, },
@ -182,9 +180,7 @@ class ProblemPage extends StatelessWidget {
); );
} }
//
Future<bool> _showDeleteConfirmationDialog(Problem problem) async { Future<bool> _showDeleteConfirmationDialog(Problem problem) async {
// 使 Get.bottomSheet
return await Get.bottomSheet<bool>( return await Get.bottomSheet<bool>(
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 16.0), padding: EdgeInsets.symmetric(horizontal: 16.0),
@ -200,7 +196,6 @@ class ProblemPage extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
//
SizedBox(height: 16), SizedBox(height: 16),
Text( Text(
'确认删除', '确认删除',
@ -208,14 +203,12 @@ class ProblemPage extends StatelessWidget {
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
SizedBox(height: 8), SizedBox(height: 8),
//
Text( Text(
'确定要删除这个问题吗?此操作不可撤销。', '确定要删除这个问题吗?此操作不可撤销。',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.grey[600]), style: TextStyle(fontSize: 14, color: Colors.grey[600]),
), ),
SizedBox(height: 24), SizedBox(height: 24),
//
ElevatedButton( ElevatedButton(
onPressed: () => Get.back(result: true), onPressed: () => Get.back(result: true),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
@ -231,7 +224,6 @@ class ProblemPage extends StatelessWidget {
), ),
), ),
SizedBox(height: 8), SizedBox(height: 8),
//
TextButton( TextButton(
onPressed: () => Get.back(result: false), onPressed: () => Get.back(result: false),
style: TextButton.styleFrom( style: TextButton.styleFrom(
@ -247,7 +239,7 @@ class ProblemPage extends StatelessWidget {
), ),
), ),
), ),
isDismissible: false, // isDismissible: false,
) ?? ) ??
false; false;
} }

2
pubspec.lock

@ -74,7 +74,7 @@ packages:
source: hosted source: hosted
version: "0.3.4+2" version: "0.3.4+2"
crypto: crypto:
dependency: transitive dependency: "direct main"
description: description:
name: crypto name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"

1
pubspec.yaml

@ -8,6 +8,7 @@ environment:
dependencies: dependencies:
connectivity_plus: ^6.1.5 connectivity_plus: ^6.1.5
crypto: ^3.0.6
dio: ^5.9.0 dio: ^5.9.0
flutter: flutter:
sdk: flutter sdk: flutter

Loading…
Cancel
Save