mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
push fixes and merge
This commit is contained in:
@ -13,6 +13,7 @@ import 'package:syncrow_web/pages/routiens/models/routine_model.dart';
|
|||||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
import 'package:syncrow_web/services/routines_api.dart';
|
import 'package:syncrow_web/services/routines_api.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/snack_bar.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
part 'routine_event.dart';
|
part 'routine_event.dart';
|
||||||
@ -46,6 +47,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
on<UpdateAutomation>(_onUpdateAutomation);
|
on<UpdateAutomation>(_onUpdateAutomation);
|
||||||
on<TriggerSwitchTabsEvent>(_triggerSwitchTabsEvent);
|
on<TriggerSwitchTabsEvent>(_triggerSwitchTabsEvent);
|
||||||
on<CreateNewRoutineViewEvent>(_createNewRoutineViewEvent);
|
on<CreateNewRoutineViewEvent>(_createNewRoutineViewEvent);
|
||||||
|
on<ResetErrorMessage>(_resetErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _triggerSwitchTabsEvent(
|
FutureOr<void> _triggerSwitchTabsEvent(
|
||||||
@ -61,6 +63,13 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_resetErrorMessage(
|
||||||
|
ResetErrorMessage event,
|
||||||
|
Emitter<RoutineState> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(errorMessage: ''));
|
||||||
|
}
|
||||||
|
|
||||||
FutureOr<void> _createNewRoutineViewEvent(
|
FutureOr<void> _createNewRoutineViewEvent(
|
||||||
CreateNewRoutineViewEvent event,
|
CreateNewRoutineViewEvent event,
|
||||||
Emitter<RoutineState> emit,
|
Emitter<RoutineState> emit,
|
||||||
@ -216,10 +225,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
emit(state.copyWith(selectedIcon: event.icon));
|
emit(state.copyWith(selectedIcon: event.icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isFirstActionDelay(List<Map<String, dynamic>> actions) {
|
// bool _isFirstActionDelay(List<Map<String, dynamic>> actions) {
|
||||||
if (actions.isEmpty) return false;
|
// if (actions.isEmpty) return false;
|
||||||
return actions.first['deviceId'] == 'delay';
|
// return actions.first['deviceId'] == 'delay';
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool _isLastActionDelay(List<Map<String, dynamic>> actions) {
|
bool _isLastActionDelay(List<Map<String, dynamic>> actions) {
|
||||||
if (actions.isEmpty) return false;
|
if (actions.isEmpty) return false;
|
||||||
@ -230,17 +239,18 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
||||||
try {
|
try {
|
||||||
// Check if first action is delay
|
// Check if first action is delay
|
||||||
if (_isFirstActionDelay(state.thenItems)) {
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
// emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the first action',
|
// errorMessage: 'Cannot have delay as the first action',
|
||||||
isLoading: false,
|
// isLoading: false,
|
||||||
));
|
// ));
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the last action',
|
errorMessage:
|
||||||
|
'A delay condition cannot be the only or the last action',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
@ -320,21 +330,26 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage: 'Automation name is required',
|
errorMessage: 'Automation name is required',
|
||||||
));
|
));
|
||||||
|
CustomSnackBar.redSnackBar('Automation name is required');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_isFirstActionDelay(state.thenItems)) {
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
// emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the first action',
|
// errorMessage: 'Cannot have delay as the first action',
|
||||||
isLoading: false,
|
// isLoading: false,
|
||||||
));
|
// ));
|
||||||
return;
|
// CustomSnackBar.redSnackBar('Cannot have delay as the first action');
|
||||||
}
|
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the last action',
|
errorMessage:
|
||||||
|
'A delay condition cannot be the only or the last action',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
|
CustomSnackBar.redSnackBar('Cannot have delay as the last action');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
@ -433,12 +448,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: result['message'],
|
errorMessage: result['message'],
|
||||||
));
|
));
|
||||||
|
CustomSnackBar.redSnackBar('Something went wrong');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: 'Something went wrong',
|
errorMessage: 'Something went wrong',
|
||||||
));
|
));
|
||||||
|
CustomSnackBar.redSnackBar('Something went wrong');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,18 +867,19 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
UpdateScene event, Emitter<RoutineState> emit) async {
|
UpdateScene event, Emitter<RoutineState> emit) async {
|
||||||
try {
|
try {
|
||||||
// Check if first action is delay
|
// Check if first action is delay
|
||||||
if (_isFirstActionDelay(state.thenItems)) {
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
// emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the first action',
|
// errorMessage: 'Cannot have delay as the first action',
|
||||||
isLoading: false,
|
// isLoading: false,
|
||||||
));
|
// ));
|
||||||
|
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the last action',
|
errorMessage:
|
||||||
|
'A delay condition cannot be the only or the last action',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
@ -943,13 +961,13 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_isFirstActionDelay(state.thenItems)) {
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
// emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the first action',
|
// errorMessage: 'Cannot have delay as the first action',
|
||||||
isLoading: false,
|
// isLoading: false,
|
||||||
));
|
// ));
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
|
@ -206,3 +206,5 @@ class FetchDevicesInRoutine extends RoutineEvent {}
|
|||||||
class ResetRoutineState extends RoutineEvent {}
|
class ResetRoutineState extends RoutineEvent {}
|
||||||
|
|
||||||
class ClearFunctions extends RoutineEvent {}
|
class ClearFunctions extends RoutineEvent {}
|
||||||
|
|
||||||
|
class ResetErrorMessage extends RoutineEvent {}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
@ -6,6 +8,7 @@ import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
|
|||||||
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class SaveRoutineHelper {
|
class SaveRoutineHelper {
|
||||||
static Future<void> showSaveRoutineDialog(BuildContext context) async {
|
static Future<void> showSaveRoutineDialog(BuildContext context) async {
|
||||||
@ -98,18 +101,29 @@ class SaveRoutineHelper {
|
|||||||
final functions =
|
final functions =
|
||||||
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: SvgPicture.asset(
|
leading: item['type'] == 'tap_to_run'
|
||||||
item['imagePath'],
|
? Image.memory(
|
||||||
width: 22,
|
base64Decode(item['icon']),
|
||||||
height: 22,
|
width: 22,
|
||||||
|
height: 22,
|
||||||
|
)
|
||||||
|
: SvgPicture.asset(
|
||||||
|
item['imagePath'],
|
||||||
|
width: 22,
|
||||||
|
height: 22,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
item['title'],
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
title:
|
|
||||||
Text(item['title'], style: const TextStyle(fontSize: 14)),
|
|
||||||
subtitle: Wrap(
|
subtitle: Wrap(
|
||||||
children: functions
|
children: functions
|
||||||
.map((f) => Text(
|
.map((f) => Text(
|
||||||
'${f.operationName}: ${f.value}, ',
|
'${f.operationName}: ${f.value}, ',
|
||||||
style: const TextStyle(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.grayColor, fontSize: 8),
|
color: ColorsManager.grayColor, fontSize: 8),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
@ -124,17 +138,17 @@ class SaveRoutineHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (state.errorMessage != null || state.errorMessage!.isNotEmpty)
|
// if (state.errorMessage != null || state.errorMessage!.isNotEmpty)
|
||||||
Padding(
|
// Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
// padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
// child: Text(
|
||||||
state.errorMessage!,
|
// state.errorMessage!,
|
||||||
style: const TextStyle(color: Colors.red),
|
// style: const TextStyle(color: Colors.red),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
DialogFooter(
|
DialogFooter(
|
||||||
onCancel: () => Navigator.pop(context),
|
onCancel: () => Navigator.pop(context),
|
||||||
onConfirm: () {
|
onConfirm: () async {
|
||||||
if (state.isAutomation) {
|
if (state.isAutomation) {
|
||||||
if (state.automationId != null) {
|
if (state.automationId != null) {
|
||||||
context.read<RoutineBloc>().add(const UpdateAutomation());
|
context.read<RoutineBloc>().add(const UpdateAutomation());
|
||||||
@ -148,10 +162,9 @@ class SaveRoutineHelper {
|
|||||||
context.read<RoutineBloc>().add(const CreateSceneEvent());
|
context.read<RoutineBloc>().add(const CreateSceneEvent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (context.read<RoutineBloc>().state.errorMessage == null ||
|
// if (state.errorMessage == null || state.errorMessage!.isEmpty) {
|
||||||
context.read<RoutineBloc>().state.errorMessage!.isEmpty) {
|
Navigator.pop(context);
|
||||||
Navigator.pop(context);
|
// }
|
||||||
}
|
|
||||||
},
|
},
|
||||||
isConfirmEnabled: true,
|
isConfirmEnabled: true,
|
||||||
),
|
),
|
||||||
|
@ -35,6 +35,20 @@ class DraggableCard extends StatelessWidget {
|
|||||||
final deviceFunctions =
|
final deviceFunctions =
|
||||||
state.selectedFunctions[deviceData['uniqueCustomId']] ?? [];
|
state.selectedFunctions[deviceData['uniqueCustomId']] ?? [];
|
||||||
|
|
||||||
|
int index = state.thenItems.indexWhere(
|
||||||
|
(item) => item['uniqueCustomId'] == deviceData['uniqueCustomId']);
|
||||||
|
|
||||||
|
if (index != -1) {
|
||||||
|
return _buildCardContent(context, deviceFunctions, padding: padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ifIndex = state.ifItems.indexWhere(
|
||||||
|
(item) => item['uniqueCustomId'] == deviceData['uniqueCustomId']);
|
||||||
|
|
||||||
|
if (ifIndex != -1) {
|
||||||
|
return _buildCardContent(context, deviceFunctions, padding: padding);
|
||||||
|
}
|
||||||
|
|
||||||
return Draggable<Map<String, dynamic>>(
|
return Draggable<Map<String, dynamic>>(
|
||||||
data: deviceData,
|
data: deviceData,
|
||||||
feedback: Transform.rotate(
|
feedback: Transform.rotate(
|
||||||
@ -79,17 +93,13 @@ class DraggableCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: imagePath.contains('.svg')
|
child: deviceData['type'] == 'tap_to_run'
|
||||||
? SvgPicture.asset(
|
? Image.memory(
|
||||||
imagePath,
|
base64Decode(deviceData['icon']),
|
||||||
)
|
)
|
||||||
: imagePath.contains('.png')
|
: SvgPicture.asset(
|
||||||
? Image.asset(
|
imagePath,
|
||||||
imagePath,
|
),
|
||||||
)
|
|
||||||
: Image.memory(
|
|
||||||
base64Decode(imagePath),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -43,6 +43,13 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
return Wrap(
|
return Wrap(
|
||||||
runSpacing: 16,
|
runSpacing: 16,
|
||||||
children: [
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
state.errorMessage ?? '',
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
@ -214,6 +221,7 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
}
|
}
|
||||||
// final result =
|
// final result =
|
||||||
// await
|
// await
|
||||||
|
BlocProvider.of<RoutineBloc>(context).add(ResetErrorMessage());
|
||||||
SaveRoutineHelper.showSaveRoutineDialog(context);
|
SaveRoutineHelper.showSaveRoutineDialog(context);
|
||||||
// if (result != null && result) {
|
// if (result != null && result) {
|
||||||
// BlocProvider.of<RoutineBloc>(context).add(
|
// BlocProvider.of<RoutineBloc>(context).add(
|
||||||
@ -341,6 +349,7 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
}
|
}
|
||||||
// final result =
|
// final result =
|
||||||
// await
|
// await
|
||||||
|
BlocProvider.of<RoutineBloc>(context).add(ResetErrorMessage());
|
||||||
SaveRoutineHelper.showSaveRoutineDialog(context);
|
SaveRoutineHelper.showSaveRoutineDialog(context);
|
||||||
// if (result != null && result) {
|
// if (result != null && result) {
|
||||||
// BlocProvider.of<RoutineBloc>(context).add(
|
// BlocProvider.of<RoutineBloc>(context).add(
|
||||||
|
@ -152,6 +152,12 @@ class ThenContainer extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mutableData['type'] == 'automation') {
|
if (mutableData['type'] == 'automation') {
|
||||||
|
int index = state.thenItems.indexWhere(
|
||||||
|
(item) => item['deviceId'] == mutableData['deviceId']);
|
||||||
|
if (index != -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final result = await showDialog<bool>(
|
final result = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => AutomationDialog(
|
builder: (BuildContext context) => AutomationDialog(
|
||||||
@ -172,6 +178,11 @@ class ThenContainer extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mutableData['type'] == 'tap_to_run' && state.isAutomation) {
|
if (mutableData['type'] == 'tap_to_run' && state.isAutomation) {
|
||||||
|
int index = state.thenItems.indexWhere(
|
||||||
|
(item) => item['deviceId'] == mutableData['deviceId']);
|
||||||
|
if (index != -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
context.read<RoutineBloc>().add(AddToThenContainer({
|
context.read<RoutineBloc>().add(AddToThenContainer({
|
||||||
...mutableData,
|
...mutableData,
|
||||||
'imagePath': mutableData['imagePath'] ?? Assets.logo,
|
'imagePath': mutableData['imagePath'] ?? Assets.logo,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/navigation_service.dart';
|
import 'package:syncrow_web/utils/navigation_service.dart';
|
||||||
|
|
||||||
class CustomSnackBar {
|
class CustomSnackBar {
|
||||||
@ -11,6 +12,35 @@ class CustomSnackBar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static redSnackBar(String message) {
|
||||||
|
final key = NavigationService.snackbarKey;
|
||||||
|
BuildContext? currentContext = key?.currentContext;
|
||||||
|
if (key != null && currentContext != null) {
|
||||||
|
final snackBar = SnackBar(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
backgroundColor: ColorsManager.red,
|
||||||
|
content: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.check_circle,
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
message,
|
||||||
|
style: Theme.of(currentContext)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
key.currentState?.showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static greenSnackBar(String message) {
|
static greenSnackBar(String message) {
|
||||||
final key = NavigationService.snackbarKey;
|
final key = NavigationService.snackbarKey;
|
||||||
BuildContext? currentContext = key?.currentContext;
|
BuildContext? currentContext = key?.currentContext;
|
||||||
@ -29,8 +59,10 @@ class CustomSnackBar {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
message,
|
message,
|
||||||
style: Theme.of(currentContext).textTheme.bodySmall!.copyWith(
|
style: Theme.of(currentContext)
|
||||||
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user