Working auth and added shared notes
This commit is contained in:
+109
-62
@@ -2,58 +2,104 @@ import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:helpers/helpers/print.dart';
|
||||
import 'package:jwt_decoder/jwt_decoder.dart';
|
||||
import 'package:rluv/global/store.dart';
|
||||
|
||||
class Api {
|
||||
static final Api _instance = Api._internal();
|
||||
import '../models/token.dart';
|
||||
|
||||
factory Api() {
|
||||
if (_instance.initDone) return _instance;
|
||||
|
||||
_instance.initDone = true;
|
||||
_instance.dio = Dio();
|
||||
_instance.dio.options.baseUrl = "http://localhost:8081/";
|
||||
// _instance.dio.options.baseUrl = "https://rluv.fosscat.com/";
|
||||
_instance.dio.interceptors.add(
|
||||
LoggingInterceptor(),
|
||||
// InterceptorsWrapper(
|
||||
// onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
|
||||
// // Do something before request is sent.
|
||||
// // If you want to resolve the request with custom data,
|
||||
// // you can resolve a `Response` using `handler.resolve(response)`.
|
||||
// // If you want to reject the request with a error message,
|
||||
// // you can reject with a `DioException` using `handler.reject(dioError)`.
|
||||
// return handler.next(options);
|
||||
// },
|
||||
// onResponse: (Response response, ResponseInterceptorHandler handler) {
|
||||
// if (response.statusCode != null &&
|
||||
// response.statusCode! < 500 &&
|
||||
// response.statusCode! >= 400) {
|
||||
// return handler.reject(DioException.badResponse(
|
||||
// requestOptions: RequestOptions(),
|
||||
// response: response,
|
||||
// statusCode: response.statusCode!));
|
||||
// }
|
||||
// // Do something with response data.
|
||||
// // If you want to reject the request with a error message,
|
||||
// // you can reject a `DioException` object using `handler.reject(dioError)`.
|
||||
// return handler.next(response);
|
||||
// },
|
||||
// onError: (DioException e, ErrorInterceptorHandler handler) {
|
||||
// printPink(e);
|
||||
// // Do something with response error.
|
||||
// // If you want to resolve the request with some custom data,
|
||||
// // you can resolve a `Response` object using `handler.resolve(response)`.
|
||||
// return handler.next(e);
|
||||
// },
|
||||
// ),
|
||||
);
|
||||
return _instance;
|
||||
final tokenProvider = StateProvider<Token?>((ref) {
|
||||
final jwt = ref.watch(jwtProvider);
|
||||
printLime('Current token: $jwt');
|
||||
if (jwt == null) return null;
|
||||
try {
|
||||
return Token.fromJson(JwtDecoder.decode(jwt));
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
Api._internal();
|
||||
});
|
||||
|
||||
bool initDone = false;
|
||||
late final Dio dio;
|
||||
final jwtProvider = StateNotifierProvider<_JwtNotifier, String?>((ref) {
|
||||
final prefs = ref.watch(prefsProvider);
|
||||
final jwt = prefs?.getString('jwt');
|
||||
return _JwtNotifier(ref, jwt);
|
||||
});
|
||||
|
||||
class _JwtNotifier extends StateNotifier<String?> {
|
||||
_JwtNotifier(this.ref, String? jwt) : super(null) {
|
||||
if (jwt != null) {
|
||||
setToken(jwt);
|
||||
}
|
||||
}
|
||||
|
||||
final StateNotifierProviderRef ref;
|
||||
|
||||
void setToken(String jwt) {
|
||||
state = jwt;
|
||||
printCyan('Loaded jwt into client: $jwt');
|
||||
ref.read(prefsProvider)?.setString('jwt', jwt);
|
||||
}
|
||||
|
||||
void revokeToken() {
|
||||
printCyan('jwt token revoked');
|
||||
state = null;
|
||||
ref.read(prefsProvider)?.remove('jwt');
|
||||
}
|
||||
}
|
||||
|
||||
final apiProvider = StateNotifierProvider<_ApiNotifier, Dio>((ref) {
|
||||
return _ApiNotifier(ref, Dio());
|
||||
});
|
||||
|
||||
class _ApiNotifier extends StateNotifier<Dio> {
|
||||
_ApiNotifier(this.ref, this.dio) : super(dio) {
|
||||
// dio.options.baseUrl = "https://fe7d-136-36-2-234.ngrok-free.app";
|
||||
// dio.options.baseUrl = "http://localhost:8081/";
|
||||
dio.options.baseUrl = "https://rluv.fosscat.com/";
|
||||
dio.interceptors.addAll([
|
||||
InterceptorsWrapper(onRequest:
|
||||
(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
final jwt = ref.read(jwtProvider);
|
||||
if (jwt != null) {
|
||||
options.headers['token'] = jwt;
|
||||
}
|
||||
return handler.next(options);
|
||||
}, onResponse: (Response response, ResponseInterceptorHandler handler) {
|
||||
if (response.statusCode != null) {
|
||||
if (response.statusCode == 200) {
|
||||
try {
|
||||
if ((response.data as Map<String, dynamic>)
|
||||
.containsKey('success')) {
|
||||
if (!response.data['success']) return handler.next(response);
|
||||
}
|
||||
if ((response.data as Map<String, dynamic>)
|
||||
.containsKey('token')) {
|
||||
final jwt = response.data['token'];
|
||||
if (jwt != null) {
|
||||
ref.read(jwtProvider.notifier).setToken(jwt);
|
||||
// ref.read(tokenProvider.notifier).state =
|
||||
// Token.fromJson(jwtData);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
printRed('Error in interceptor for token: $err');
|
||||
return handler.next(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
return handler.next(response);
|
||||
}, onError: (DioException err, ErrorInterceptorHandler handler) {
|
||||
if (err.response?.statusCode == 403) {
|
||||
ref.read(jwtProvider.notifier).revokeToken();
|
||||
}
|
||||
}),
|
||||
_LoggingInterceptor(),
|
||||
]);
|
||||
}
|
||||
|
||||
final Dio dio;
|
||||
final StateNotifierProviderRef ref;
|
||||
|
||||
Future<Map<String, dynamic>?> get(String path) async {
|
||||
try {
|
||||
@@ -64,7 +110,6 @@ class Api {
|
||||
}
|
||||
return null;
|
||||
} catch (err) {
|
||||
printRed('Error in get: $err');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -79,7 +124,6 @@ class Api {
|
||||
}
|
||||
return null;
|
||||
} catch (err) {
|
||||
printRed('Error in put: $err');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -94,7 +138,6 @@ class Api {
|
||||
}
|
||||
return null;
|
||||
} catch (err) {
|
||||
printRed('Error in put: $err');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -109,14 +152,13 @@ class Api {
|
||||
}
|
||||
return null;
|
||||
} catch (err) {
|
||||
printRed('Error in delete: $err');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LoggingInterceptor extends Interceptor {
|
||||
LoggingInterceptor();
|
||||
class _LoggingInterceptor extends Interceptor {
|
||||
_LoggingInterceptor();
|
||||
|
||||
@override
|
||||
Future onRequest(
|
||||
@@ -134,7 +176,7 @@ class LoggingInterceptor extends Interceptor {
|
||||
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
logPrint('///*** ERROR RESPONSE ***\\\\\\');
|
||||
printRed('///*** ERROR RESPONSE ***\\\\\\');
|
||||
logPrint('URI: ${err.requestOptions.uri}');
|
||||
if (err.response != null) {
|
||||
logPrint('STATUS CODE: ${err.response?.statusCode?.toString()}');
|
||||
@@ -167,15 +209,20 @@ class LoggingInterceptor extends Interceptor {
|
||||
}
|
||||
}
|
||||
|
||||
void printJson(Map<String, dynamic>? s) {
|
||||
if (kDebugMode) {
|
||||
if (s == null) {
|
||||
printAmber({});
|
||||
return;
|
||||
void printJson(dynamic s) {
|
||||
try {
|
||||
final data = (s as Map<String, dynamic>?);
|
||||
if (kDebugMode) {
|
||||
if (data == null) {
|
||||
printAmber({});
|
||||
return;
|
||||
}
|
||||
JsonEncoder encoder = const JsonEncoder.withIndent(' ');
|
||||
String prettyprint = encoder.convert(s);
|
||||
printAmber(prettyprint);
|
||||
}
|
||||
JsonEncoder encoder = const JsonEncoder.withIndent(' ');
|
||||
String prettyprint = encoder.convert(s);
|
||||
printAmber(prettyprint);
|
||||
} catch (_) {
|
||||
printAmber(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+105
-98
@@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:helpers/helpers/print.dart';
|
||||
import 'package:rluv/global/api.dart';
|
||||
@@ -12,116 +13,120 @@ import '../models/shared_note.dart';
|
||||
import '../models/transaction_model.dart';
|
||||
import '../models/user.dart';
|
||||
|
||||
class Store {
|
||||
static final Store _instance = Store._internal();
|
||||
bool _initDone = false;
|
||||
SharedPreferences? prefs;
|
||||
StateProvider<SharedPreferences?> prefsProvider =
|
||||
StateProvider<SharedPreferences?>((ref) => null);
|
||||
|
||||
factory Store() {
|
||||
if (_instance._initDone) {
|
||||
return _instance;
|
||||
}
|
||||
_instance._initDone = true;
|
||||
SharedPreferences.getInstance().then(
|
||||
(value) => _instance.prefs = value,
|
||||
);
|
||||
_instance.dashboardProvider =
|
||||
StateNotifierProvider<DashBoardStateNotifier, Map<String, dynamic>?>(
|
||||
(ref) {
|
||||
final family = ref.watch(_instance.familyProvider).valueOrNull;
|
||||
return DashBoardStateNotifier(family);
|
||||
},
|
||||
);
|
||||
// _instance.dashboardProvider = FutureProvider<Map<String, dynamic>?>(
|
||||
// (ref) async {
|
||||
// final family = await ref.watch(_instance.familyProvider.future);
|
||||
// return Api().get("dashboard/${family.id}");
|
||||
// },
|
||||
// );
|
||||
_instance.budgetProvider = Provider<Budget?>(
|
||||
(ref) {
|
||||
final dash = ref.watch(_instance.dashboardProvider);
|
||||
if (dash == null) return null;
|
||||
final budgetData = dash['budget'] as Map<String, dynamic>?;
|
||||
if (budgetData == null) return null;
|
||||
return Budget.fromJson(budgetData);
|
||||
},
|
||||
);
|
||||
_instance.budgetCategoriesProvider = Provider<List<BudgetCategory>>((ref) {
|
||||
final dash = ref.watch(_instance.dashboardProvider);
|
||||
if (dash == null) return [];
|
||||
final categoriesData = dash['budget_categories'] as List<dynamic>;
|
||||
final categories = categoriesData
|
||||
.map(
|
||||
(e) => BudgetCategory.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList();
|
||||
if (_instance.prefs != null) {
|
||||
final budgetJson = jsonEncode({'budget_categories': categoriesData});
|
||||
printBlue('updated prefs stored categories');
|
||||
_instance.prefs!.setString('budget_categories', budgetJson);
|
||||
}
|
||||
return categories;
|
||||
});
|
||||
_instance.transactionsProvider = Provider<List<Transaction>>((ref) {
|
||||
final dash = ref.watch(_instance.dashboardProvider);
|
||||
if (dash == null) return [];
|
||||
final transactions = dash['transactions'] as List<dynamic>;
|
||||
return transactions
|
||||
.map(
|
||||
(e) => Transaction.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList();
|
||||
});
|
||||
return _instance;
|
||||
}
|
||||
// final StateProvider<Token?> tokenProvider = StateProvider<Token?>((ref) {
|
||||
// // final tokenStr = prefs.getString('jwt');
|
||||
// // if (tokenStr != null) {
|
||||
// // return Token.fromJson(jsonDecode(tokenStr));
|
||||
// // }
|
||||
// return null;
|
||||
// });
|
||||
|
||||
Store._internal();
|
||||
final Provider<User?> userProvider = Provider<User?>(
|
||||
(ref) {
|
||||
final dash = ref.watch(dashboardProvider);
|
||||
if (dash == null) return null;
|
||||
final userData = dash['user'] as Map<String, dynamic>;
|
||||
return User.fromJson(userData);
|
||||
},
|
||||
);
|
||||
|
||||
final FutureProvider<User> userProvider = FutureProvider<User>(
|
||||
(ref) {
|
||||
return User(
|
||||
id: 0,
|
||||
budgetId: 1,
|
||||
createdAt: DateTime.now(),
|
||||
familyId: 1,
|
||||
lastActivityAt: DateTime.now(),
|
||||
name: 'TEMP',
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
},
|
||||
);
|
||||
final Provider<FamilyModel?> familyProvider = Provider<FamilyModel?>(
|
||||
(ref) {
|
||||
final dash = ref.watch(dashboardProvider);
|
||||
if (dash == null) return null;
|
||||
final familyData = dash['family'] as Map<String, dynamic>;
|
||||
return FamilyModel.fromJson(familyData);
|
||||
},
|
||||
);
|
||||
|
||||
final FutureProvider<FamilyModel> familyProvider =
|
||||
FutureProvider<FamilyModel>((ref) => FamilyModel(
|
||||
id: 1,
|
||||
budgetId: 1,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now()));
|
||||
final Provider<List<BudgetCategory>> budgetCategoriesProvider =
|
||||
Provider<List<BudgetCategory>>((ref) {
|
||||
final dash = ref.watch(dashboardProvider);
|
||||
if (dash == null) return [];
|
||||
final categoriesData = dash['budget_categories'] as List<dynamic>;
|
||||
final categories = categoriesData
|
||||
.map(
|
||||
(e) => BudgetCategory.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList();
|
||||
|
||||
late final Provider<List<BudgetCategory>> budgetCategoriesProvider;
|
||||
late final Provider<Budget?> budgetProvider;
|
||||
late final Provider<List<Transaction>> transactionsProvider;
|
||||
late final Provider<List<SharedNote>> sharedNotesProvider;
|
||||
// late final FutureProvider<Map<String, dynamic>?> dashboardProvider;
|
||||
final prefs = ref.read(prefsProvider);
|
||||
final budgetJson = jsonEncode({'budget_categories': categoriesData});
|
||||
printBlue('updated prefs stored categories');
|
||||
prefs?.setString('budget_categories', budgetJson);
|
||||
|
||||
late final StateNotifierProvider<DashBoardStateNotifier,
|
||||
Map<String, dynamic>?> dashboardProvider;
|
||||
void fetchDashboard() {}
|
||||
}
|
||||
return categories;
|
||||
});
|
||||
|
||||
final Provider<Budget?> budgetProvider = Provider<Budget?>(
|
||||
(ref) {
|
||||
final dash = ref.watch(dashboardProvider);
|
||||
if (dash == null) return null;
|
||||
final budgetData = dash['budget'] as Map<String, dynamic>?;
|
||||
if (budgetData == null) return null;
|
||||
return Budget.fromJson(budgetData);
|
||||
},
|
||||
);
|
||||
|
||||
final Provider<List<Transaction>> transactionsProvider =
|
||||
Provider<List<Transaction>>((ref) {
|
||||
final dash = ref.watch(dashboardProvider);
|
||||
if (dash == null) return [];
|
||||
final transactions = dash['transactions'] as List<dynamic>;
|
||||
return transactions
|
||||
.map(
|
||||
(e) => Transaction.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList();
|
||||
});
|
||||
|
||||
final Provider<List<SharedNote>> sharedNotesProvider =
|
||||
Provider<List<SharedNote>>(
|
||||
(ref) {
|
||||
final dash = ref.watch(dashboardProvider);
|
||||
if (dash == null) return [];
|
||||
final sharedNotes = dash['shared_notes'] as List<dynamic>;
|
||||
return sharedNotes
|
||||
.map(
|
||||
(e) => SharedNote.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList();
|
||||
},
|
||||
);
|
||||
|
||||
final dashboardProvider =
|
||||
StateNotifierProvider<DashBoardStateNotifier, Map<String, dynamic>?>(
|
||||
(ref) {
|
||||
return DashBoardStateNotifier(ref);
|
||||
},
|
||||
);
|
||||
|
||||
final loadingStateProvider = StateProvider<bool>(
|
||||
(ref) => false,
|
||||
);
|
||||
|
||||
class DashBoardStateNotifier extends StateNotifier<Map<String, dynamic>?> {
|
||||
DashBoardStateNotifier(FamilyModel? family) : super(null) {
|
||||
fetchDashboard(family);
|
||||
}
|
||||
DashBoardStateNotifier(this.ref) : super(null);
|
||||
|
||||
Future fetchDashboard(FamilyModel? family) async {
|
||||
if (family == null) {
|
||||
printPink('Unable to get dashboard');
|
||||
StateNotifierProviderRef ref;
|
||||
|
||||
Future<void> fetchDashboard() async {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => ref.read(loadingStateProvider.notifier).state = true,
|
||||
);
|
||||
final token = ref.read(tokenProvider);
|
||||
if (token?.familyId == null) {
|
||||
printPink('No token, cannot fetch dashboard');
|
||||
return;
|
||||
}
|
||||
printAmber('Fetching dashboard');
|
||||
state = await Api().get("dashboard/${family.id}");
|
||||
state = await ref.read(apiProvider.notifier).get("dashboard");
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => ref.read(loadingStateProvider.notifier).state = false,
|
||||
);
|
||||
}
|
||||
|
||||
void update(Map<String, dynamic> data) {
|
||||
@@ -136,6 +141,7 @@ class DashBoardStateNotifier extends StateNotifier<Map<String, dynamic>?> {
|
||||
switch (key) {
|
||||
case 'transactions':
|
||||
case 'budget_categories':
|
||||
case 'shared_notes':
|
||||
final subStateList = state![key] as List<dynamic>;
|
||||
final subStateListObj = subStateList
|
||||
.map(
|
||||
@@ -171,6 +177,7 @@ class DashBoardStateNotifier extends StateNotifier<Map<String, dynamic>?> {
|
||||
switch (key) {
|
||||
case 'transactions':
|
||||
case 'budget_categories':
|
||||
case 'shared_noted':
|
||||
final subStateList = state![key] as List<dynamic>;
|
||||
final newState = state;
|
||||
subStateList.add(data.values.first);
|
||||
|
||||
@@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class Styles {
|
||||
// Theme Colors
|
||||
static const Color purpleNurple = Color(0xffA188A6);
|
||||
static const Color purpleNurple = Color(0xFFA188A6);
|
||||
static const Color deepPurpleNurple = Color(0xFF977C9C);
|
||||
static const Color sunflower = Color(0xffFFEE88);
|
||||
static const Color electricBlue = Color(0xFF19647E);
|
||||
static const Color blushingPink = Color(0xFFE78F8E);
|
||||
@@ -10,7 +11,7 @@ class Styles {
|
||||
static const Color emptyBarGrey = Color(0xFFC8C8C8);
|
||||
static const Color lavender = Color(0xFFB8B8FF);
|
||||
static const Color washedStone = Color(0xFFD9D9D9);
|
||||
|
||||
static const Color flounderBlue = Color(0xFFA7E2E3);
|
||||
// Income Colors
|
||||
static const Color incomeBlue = Color(0xFFB8B8FF);
|
||||
static const Color incomeGreen = Color(0xFF0FA102);
|
||||
@@ -43,6 +44,10 @@ class Styles {
|
||||
Color(0xFFFFB563)
|
||||
];
|
||||
|
||||
// Button Styles
|
||||
static const Color disabledButton = Color(0xFFA8A8A8);
|
||||
static const Color disabledButtonText = Color(0xFFD9D9D9);
|
||||
|
||||
// Widget Styles
|
||||
static BoxDecoration boxLavenderBubble = BoxDecoration(
|
||||
color: Styles.lavender,
|
||||
|
||||
@@ -2,8 +2,11 @@ import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:helpers/helpers/print.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:rluv/global/styles.dart';
|
||||
import 'package:rluv/main.dart';
|
||||
|
||||
String formatDate(DateTime time) {
|
||||
return DateFormat('EEEE, dd MMMM yyyy').format(time);
|
||||
@@ -70,3 +73,59 @@ void setDevicePortraitOrientation() {
|
||||
DeviceOrientation.portraitDown,
|
||||
]);
|
||||
}
|
||||
|
||||
enum SnackType { info, success, error }
|
||||
|
||||
void showSnack(
|
||||
{required WidgetRef ref,
|
||||
required String text,
|
||||
SnackType type = SnackType.info,
|
||||
Duration duration = const Duration(seconds: 2)}) {
|
||||
final messengerKey = ref.read(scaffoldMessengerKeyProvider);
|
||||
if (messengerKey.currentState == null) {
|
||||
printPink('Cannot show snackbar, state == null');
|
||||
return;
|
||||
}
|
||||
final color = type == SnackType.info
|
||||
? Styles.washedStone
|
||||
: type == SnackType.success
|
||||
? Styles.seaweedGreen
|
||||
: Styles.expensesRed;
|
||||
final textStyle = TextStyle(
|
||||
fontSize: 16,
|
||||
color: color,
|
||||
);
|
||||
messengerKey.currentState!.showSnackBar(SnackBar(
|
||||
elevation: 8,
|
||||
backgroundColor: Styles.deepPurpleNurple,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0)),
|
||||
),
|
||||
content: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 14.0),
|
||||
child: Icon(
|
||||
type == SnackType.success ? Icons.check_circle : Icons.info,
|
||||
color: color),
|
||||
),
|
||||
Text(text, style: textStyle),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
bool isEmailValid(String email) {
|
||||
final RegExp regex =
|
||||
RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$");
|
||||
return regex.hasMatch(email);
|
||||
}
|
||||
|
||||
bool isUsernameValid(String username) {
|
||||
final RegExp regex = RegExp(r"[a-zA-Z0-9._-]");
|
||||
return regex.hasMatch(username);
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rluv/global/styles.dart';
|
||||
|
||||
class CuteDrawerButton extends StatelessWidget {
|
||||
const CuteDrawerButton(
|
||||
{super.key,
|
||||
required this.text,
|
||||
required this.color,
|
||||
required this.onPressed});
|
||||
|
||||
final String text;
|
||||
final Function onPressed;
|
||||
final Color color;
|
||||
final double borderRadius = 12.0;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
onTap: () => onPressed(),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 2.0,
|
||||
spreadRadius: 2.0,
|
||||
offset: Offset(2.5, 2.5),
|
||||
)
|
||||
],
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Styles.washedStone,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rluv/global/styles.dart';
|
||||
import 'package:rluv/global/utils.dart';
|
||||
|
||||
class UiButton extends StatefulWidget {
|
||||
const UiButton({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.color = Styles.deepPurpleNurple,
|
||||
required this.onPressed,
|
||||
this.height,
|
||||
this.width,
|
||||
this.icon,
|
||||
this.showLoading = false,
|
||||
});
|
||||
|
||||
final String text;
|
||||
final Function? onPressed;
|
||||
final Color color;
|
||||
final double? width;
|
||||
final double? height;
|
||||
final Icon? icon;
|
||||
final bool showLoading;
|
||||
|
||||
@override
|
||||
State<UiButton> createState() => _UiButtonState();
|
||||
}
|
||||
|
||||
class _UiButtonState extends State<UiButton> {
|
||||
final double borderRadius = 12.0;
|
||||
bool loading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final computedColor =
|
||||
widget.onPressed == null ? Styles.disabledButton : widget.color;
|
||||
final brightness = getBrightness(computedColor);
|
||||
return SizedBox(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
onTap: widget.onPressed == null || loading
|
||||
? null
|
||||
: widget.showLoading
|
||||
? () async {
|
||||
setState(() => loading = true);
|
||||
await widget.onPressed!();
|
||||
setState(() => loading = false);
|
||||
}
|
||||
: () => widget.onPressed!(),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: computedColor,
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 2.0,
|
||||
spreadRadius: 2.0,
|
||||
offset: Offset(2.5, 2.5),
|
||||
)
|
||||
],
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: loading
|
||||
? [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: SizedBox(
|
||||
height: 26,
|
||||
width: 26,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2.0,
|
||||
color: brightness == Brightness.dark
|
||||
? Styles.lavender
|
||||
: Styles.electricBlue)),
|
||||
),
|
||||
]
|
||||
: [
|
||||
if (widget.icon != null) widget.icon!,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
widget.text,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: widget.onPressed == null
|
||||
? Styles.disabledButtonText
|
||||
: brightness == Brightness.dark
|
||||
? Styles.washedStone
|
||||
: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user