feat: Add theme customization feature with ThemeEditorWidget
- Introduced ThemeEditorWidget for user-friendly theme selection. - Added ThemeOption class to manage theme properties. - Implemented ThemeController to handle theme application logic. - Updated ThemeCustomizer to allow external theme changes. - Refactored wave background components to support dynamic colors. - Updated various screens to utilize the new theme system. - Enhanced UI elements with consistent styling and improved responsiveness.
This commit is contained in:
parent
cd21a3ac38
commit
c78231d0fd
@ -2,39 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:marco/helpers/theme/theme_customizer.dart';
|
||||
|
||||
enum LeftBarThemeType { light, dark }
|
||||
|
||||
enum ContentThemeType { light, dark }
|
||||
|
||||
enum RightBarThemeType { light, dark }
|
||||
|
||||
enum ContentThemeColor {
|
||||
primary,
|
||||
secondary,
|
||||
success,
|
||||
info,
|
||||
warning,
|
||||
danger,
|
||||
light,
|
||||
dark,
|
||||
pink,
|
||||
green,
|
||||
red,
|
||||
brandRed,
|
||||
brandGreen;
|
||||
|
||||
Color get color {
|
||||
return (AdminTheme.theme.contentTheme.getMappedIntoThemeColor[this]
|
||||
?['color']) ??
|
||||
Colors.black;
|
||||
}
|
||||
|
||||
Color get onColor {
|
||||
return (AdminTheme.theme.contentTheme.getMappedIntoThemeColor[this]
|
||||
?['onColor']) ??
|
||||
Colors.white;
|
||||
}
|
||||
}
|
||||
|
||||
class LeftBarTheme {
|
||||
final Color background, onBackground;
|
||||
final Color labelColor;
|
||||
@ -48,16 +18,15 @@ class LeftBarTheme {
|
||||
this.activeItemBackground = const Color(0x15663399),
|
||||
});
|
||||
|
||||
//-------------------------------------- Left Bar Theme ----------------------------------------//
|
||||
|
||||
static final LeftBarTheme lightLeftBarTheme = LeftBarTheme();
|
||||
|
||||
static final LeftBarTheme darkLeftBarTheme = LeftBarTheme(
|
||||
background: const Color(0xff282c32),
|
||||
onBackground: const Color(0xffdcdcdc),
|
||||
labelColor: const Color(0xff32BFAE),
|
||||
activeItemBackground: const Color(0x1532BFAE),
|
||||
activeItemColor: const Color(0xff32BFAE));
|
||||
background: const Color(0xff282c32),
|
||||
onBackground: const Color(0xffdcdcdc),
|
||||
labelColor: const Color(0xff32BFAE),
|
||||
activeItemBackground: const Color(0x1532BFAE),
|
||||
activeItemColor: const Color(0xff32BFAE),
|
||||
);
|
||||
|
||||
static LeftBarTheme getThemeFromType(LeftBarThemeType leftBarThemeType) {
|
||||
switch (leftBarThemeType) {
|
||||
@ -78,13 +47,12 @@ class TopBarTheme {
|
||||
this.onBackground = const Color(0xff313a46),
|
||||
});
|
||||
|
||||
//-------------------------------------- Left Bar Theme ----------------------------------------//
|
||||
|
||||
static final TopBarTheme lightTopBarTheme = TopBarTheme();
|
||||
|
||||
static final TopBarTheme darkTopBarTheme = TopBarTheme(
|
||||
background: const Color(0xff2c3036),
|
||||
onBackground: const Color(0xffdcdcdc));
|
||||
background: const Color(0xff2c3036),
|
||||
onBackground: const Color(0xffdcdcdc),
|
||||
);
|
||||
}
|
||||
|
||||
class RightBarTheme {
|
||||
@ -98,19 +66,41 @@ class RightBarTheme {
|
||||
this.onDisabled = const Color(0xff313a46),
|
||||
});
|
||||
|
||||
//-------------------------------------- Left Bar Theme ----------------------------------------//
|
||||
|
||||
static final RightBarTheme lightRightBarTheme = RightBarTheme(
|
||||
disabled: const Color(0xffffffff),
|
||||
onDisabled: const Color(0xffdee2e6),
|
||||
activeSwitchBorderColor: const Color(0xff727cf5),
|
||||
inactiveSwitchBorderColor: const Color(0xffdee2e6));
|
||||
disabled: const Color(0xffffffff),
|
||||
onDisabled: const Color(0xffdee2e6),
|
||||
activeSwitchBorderColor: const Color(0xff727cf5),
|
||||
inactiveSwitchBorderColor: const Color(0xffdee2e6),
|
||||
);
|
||||
|
||||
static final RightBarTheme darkRightBarTheme = RightBarTheme(
|
||||
disabled: const Color(0xff444d57),
|
||||
activeSwitchBorderColor: const Color(0xff727cf5),
|
||||
inactiveSwitchBorderColor: const Color(0xffdee2e6),
|
||||
onDisabled: const Color(0xff515a65));
|
||||
disabled: const Color(0xff444d57),
|
||||
activeSwitchBorderColor: const Color(0xff727cf5),
|
||||
inactiveSwitchBorderColor: const Color(0xffdee2e6),
|
||||
onDisabled: const Color(0xff515a65),
|
||||
);
|
||||
}
|
||||
|
||||
enum ContentThemeColor {
|
||||
primary,
|
||||
secondary,
|
||||
success,
|
||||
info,
|
||||
warning,
|
||||
danger,
|
||||
light,
|
||||
dark,
|
||||
pink,
|
||||
green,
|
||||
red;
|
||||
|
||||
Color get color {
|
||||
return (AdminTheme.theme.contentTheme.getMappedIntoThemeColor[this]?['color']) ?? Colors.black;
|
||||
}
|
||||
|
||||
Color get onColor {
|
||||
return (AdminTheme.theme.contentTheme.getMappedIntoThemeColor[this]?['onColor']) ?? Colors.white;
|
||||
}
|
||||
}
|
||||
|
||||
class ContentTheme {
|
||||
@ -127,44 +117,15 @@ class ContentTheme {
|
||||
final Color purple, onPurple;
|
||||
final Color pink, onPink;
|
||||
final Color red, onRed;
|
||||
final Color brandRed, onBrandRed;
|
||||
final Color brandGreen, onBrandGreen;
|
||||
|
||||
final Color cardBackground, cardShadow, cardBorder, cardText, cardTextMuted;
|
||||
final Color title;
|
||||
final Color disabled, onDisabled;
|
||||
|
||||
Map<ContentThemeColor, Map<String, Color>> get getMappedIntoThemeColor {
|
||||
var c = AdminTheme.theme.contentTheme;
|
||||
return {
|
||||
ContentThemeColor.primary: {'color': c.primary, 'onColor': c.onPrimary},
|
||||
ContentThemeColor.secondary: {
|
||||
'color': c.secondary,
|
||||
'onColor': c.onSecondary
|
||||
},
|
||||
ContentThemeColor.success: {'color': c.success, 'onColor': c.onSuccess},
|
||||
ContentThemeColor.info: {'color': c.info, 'onColor': c.onInfo},
|
||||
ContentThemeColor.warning: {'color': c.warning, 'onColor': c.onWarning},
|
||||
ContentThemeColor.danger: {'color': c.danger, 'onColor': c.onDanger},
|
||||
ContentThemeColor.light: {'color': c.light, 'onColor': c.onLight},
|
||||
ContentThemeColor.dark: {'color': c.dark, 'onColor': c.onDark},
|
||||
ContentThemeColor.pink: {'color': c.pink, 'onColor': c.onPink},
|
||||
ContentThemeColor.red: {'color': c.red, 'onColor': c.onRed},
|
||||
ContentThemeColor.brandRed: {
|
||||
'color': c.brandRed,
|
||||
'onColor': c.onBrandRed
|
||||
},
|
||||
ContentThemeColor.green: {
|
||||
'color': c.brandGreen,
|
||||
'onColor': c.onBrandGreen
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ContentTheme({
|
||||
this.background = const Color(0xfffafbfe),
|
||||
this.onBackground = const Color(0xffF1F1F2),
|
||||
this.primary = const Color(0xFF49BF3C),
|
||||
this.primary = const Color(0xff663399),
|
||||
this.onPrimary = const Color(0xffffffff),
|
||||
this.secondary = const Color(0xff6c757d),
|
||||
this.onSecondary = const Color(0xffffffff),
|
||||
@ -181,13 +142,11 @@ class ContentTheme {
|
||||
this.dark = const Color(0xff313a46),
|
||||
this.onDark = const Color(0xffffffff),
|
||||
this.purple = const Color(0xff800080),
|
||||
this.onPurple = const Color(0xffFF0000),
|
||||
this.pink = const Color(0xffFF1087),
|
||||
this.onPurple = const Color(0xffffffff),
|
||||
this.pink = const Color(0xffff1087),
|
||||
this.onPink = const Color(0xffffffff),
|
||||
this.red = const Color(0xffFF0000),
|
||||
this.red = const Color(0xffff0000),
|
||||
this.onRed = const Color(0xffffffff),
|
||||
this.brandRed = const Color.fromARGB(255, 255, 0, 0),
|
||||
this.onBrandRed = const Color(0xffffffff),
|
||||
this.cardBackground = const Color(0xffffffff),
|
||||
this.cardShadow = const Color(0xffffffff),
|
||||
this.cardBorder = const Color(0xffffffff),
|
||||
@ -196,46 +155,105 @@ class ContentTheme {
|
||||
this.title = const Color(0xff6c757d),
|
||||
this.disabled = const Color(0xffffffff),
|
||||
this.onDisabled = const Color(0xffffffff),
|
||||
this.brandGreen = const Color(0xFF49BF3C),
|
||||
this.onBrandGreen = const Color(0xFFFFFFFF),
|
||||
});
|
||||
|
||||
static final ContentTheme lightContentTheme = ContentTheme(
|
||||
primary: Color(0xFF49BF3C),
|
||||
background: const Color(0xfffafbfe),
|
||||
onBackground: const Color(0xff313a46),
|
||||
cardBorder: const Color(0xffe8ecf1),
|
||||
cardBackground: const Color(0xffffffff),
|
||||
cardShadow: const Color(0xff9aa1ab),
|
||||
cardText: const Color(0xff6c757d),
|
||||
title: const Color(0xff6c757d),
|
||||
cardTextMuted: const Color(0xff98a6ad),
|
||||
brandRed: const Color.fromARGB(255, 255, 0, 0),
|
||||
onBrandRed: const Color(0xffffffff),
|
||||
);
|
||||
Map<ContentThemeColor, Map<String, Color>> get getMappedIntoThemeColor {
|
||||
return {
|
||||
ContentThemeColor.primary: {'color': primary, 'onColor': onPrimary},
|
||||
ContentThemeColor.secondary: {'color': secondary, 'onColor': onSecondary},
|
||||
ContentThemeColor.success: {'color': success, 'onColor': onSuccess},
|
||||
ContentThemeColor.info: {'color': info, 'onColor': onInfo},
|
||||
ContentThemeColor.warning: {'color': warning, 'onColor': onWarning},
|
||||
ContentThemeColor.danger: {'color': danger, 'onColor': onDanger},
|
||||
ContentThemeColor.light: {'color': light, 'onColor': onLight},
|
||||
ContentThemeColor.dark: {'color': dark, 'onColor': onDark},
|
||||
ContentThemeColor.pink: {'color': pink, 'onColor': onPink},
|
||||
ContentThemeColor.red: {'color': red, 'onColor': onRed},
|
||||
};
|
||||
}
|
||||
|
||||
static final ContentTheme darkContentTheme = ContentTheme(
|
||||
primary: Color(0xFF49BF3C),
|
||||
background: const Color(0xff343a40),
|
||||
onBackground: const Color(0xffF1F1F2),
|
||||
disabled: const Color(0xff444d57),
|
||||
onDisabled: const Color(0xff515a65),
|
||||
cardBorder: const Color(0xff464f5b),
|
||||
cardBackground: const Color(0xff37404a),
|
||||
cardShadow: const Color(0xff01030E),
|
||||
cardText: const Color(0xffaab8c5),
|
||||
title: const Color(0xffaab8c5),
|
||||
cardTextMuted: const Color(0xff8391a2),
|
||||
brandRed: const Color.fromARGB(255, 255, 0, 0),
|
||||
onBrandRed: const Color(0xffffffff),
|
||||
);
|
||||
ContentTheme copyWith({
|
||||
Color? primary,
|
||||
Color? onPrimary,
|
||||
Color? secondary,
|
||||
Color? onSecondary,
|
||||
Color? background,
|
||||
Color? onBackground,
|
||||
}) {
|
||||
return ContentTheme(
|
||||
primary: primary ?? this.primary,
|
||||
onPrimary: onPrimary ?? this.onPrimary,
|
||||
secondary: secondary ?? this.secondary,
|
||||
onSecondary: onSecondary ?? this.onSecondary,
|
||||
background: background ?? this.background,
|
||||
onBackground: onBackground ?? this.onBackground,
|
||||
success: success,
|
||||
onSuccess: onSuccess,
|
||||
danger: danger,
|
||||
onDanger: onDanger,
|
||||
warning: warning,
|
||||
onWarning: onWarning,
|
||||
info: info,
|
||||
onInfo: onInfo,
|
||||
light: light,
|
||||
onLight: onLight,
|
||||
dark: dark,
|
||||
onDark: onDark,
|
||||
purple: purple,
|
||||
onPurple: onPurple,
|
||||
pink: pink,
|
||||
onPink: onPink,
|
||||
red: red,
|
||||
onRed: onRed,
|
||||
cardBackground: cardBackground,
|
||||
cardShadow: cardShadow,
|
||||
cardBorder: cardBorder,
|
||||
cardText: cardText,
|
||||
cardTextMuted: cardTextMuted,
|
||||
title: title,
|
||||
disabled: disabled,
|
||||
onDisabled: onDisabled,
|
||||
);
|
||||
}
|
||||
|
||||
static ContentTheme withColorTheme(
|
||||
ColorThemeType colorTheme, {
|
||||
ThemeMode mode = ThemeMode.light,
|
||||
}) {
|
||||
final baseTheme = mode == ThemeMode.light
|
||||
? ContentTheme()
|
||||
: ContentTheme(
|
||||
primary: const Color(0xff32BFAE),
|
||||
background: const Color(0xff343a40),
|
||||
onBackground: const Color(0xffF1F1F2),
|
||||
cardBorder: const Color(0xff464f5b),
|
||||
cardBackground: const Color(0xff37404a),
|
||||
cardShadow: const Color(0xff01030E),
|
||||
cardText: const Color(0xffaab8c5),
|
||||
title: const Color(0xffaab8c5),
|
||||
cardTextMuted: const Color(0xff8391a2),
|
||||
);
|
||||
|
||||
switch (colorTheme) {
|
||||
case ColorThemeType.purple:
|
||||
return baseTheme.copyWith(primary: const Color(0xff663399), onPrimary: Colors.white);
|
||||
case ColorThemeType.red:
|
||||
return baseTheme.copyWith(primary: const Color(0xffff0000), onPrimary: Colors.white);
|
||||
case ColorThemeType.green:
|
||||
return baseTheme.copyWith(primary: const Color(0xff49BF3C), onPrimary: Colors.white);
|
||||
case ColorThemeType.blue:
|
||||
return baseTheme.copyWith(primary: const Color(0xff007bff), onPrimary: Colors.white);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ColorThemeType { purple, red, green, blue }
|
||||
|
||||
class AdminTheme {
|
||||
final ContentTheme contentTheme;
|
||||
final LeftBarTheme leftBarTheme;
|
||||
final RightBarTheme rightBarTheme;
|
||||
final TopBarTheme topBarTheme;
|
||||
final ContentTheme contentTheme;
|
||||
|
||||
AdminTheme({
|
||||
required this.leftBarTheme,
|
||||
@ -244,27 +262,22 @@ class AdminTheme {
|
||||
required this.contentTheme,
|
||||
});
|
||||
|
||||
//-------------------------------------- Left Bar Theme ----------------------------------------//
|
||||
|
||||
static AdminTheme theme = AdminTheme(
|
||||
leftBarTheme: LeftBarTheme.lightLeftBarTheme,
|
||||
topBarTheme: TopBarTheme.lightTopBarTheme,
|
||||
rightBarTheme: RightBarTheme.lightRightBarTheme,
|
||||
contentTheme: ContentTheme.lightContentTheme);
|
||||
leftBarTheme: LeftBarTheme.lightLeftBarTheme,
|
||||
topBarTheme: TopBarTheme.lightTopBarTheme,
|
||||
rightBarTheme: RightBarTheme.lightRightBarTheme,
|
||||
contentTheme: ContentTheme.withColorTheme(ColorThemeType.purple, mode: ThemeMode.light),
|
||||
);
|
||||
|
||||
static void setTheme() {
|
||||
final themeMode = ThemeCustomizer.instance.theme;
|
||||
final colorTheme = ThemeCustomizer.instance.colorTheme;
|
||||
|
||||
theme = AdminTheme(
|
||||
leftBarTheme: ThemeCustomizer.instance.theme == ThemeMode.dark
|
||||
? LeftBarTheme.darkLeftBarTheme
|
||||
: LeftBarTheme.lightLeftBarTheme,
|
||||
topBarTheme: ThemeCustomizer.instance.theme == ThemeMode.dark
|
||||
? TopBarTheme.darkTopBarTheme
|
||||
: TopBarTheme.lightTopBarTheme,
|
||||
rightBarTheme: ThemeCustomizer.instance.theme == ThemeMode.dark
|
||||
? RightBarTheme.darkRightBarTheme
|
||||
: RightBarTheme.lightRightBarTheme,
|
||||
contentTheme: ThemeCustomizer.instance.theme == ThemeMode.dark
|
||||
? ContentTheme.darkContentTheme
|
||||
: ContentTheme.lightContentTheme);
|
||||
leftBarTheme: themeMode == ThemeMode.dark ? LeftBarTheme.darkLeftBarTheme : LeftBarTheme.lightLeftBarTheme,
|
||||
topBarTheme: themeMode == ThemeMode.dark ? TopBarTheme.darkTopBarTheme : TopBarTheme.lightTopBarTheme,
|
||||
rightBarTheme: themeMode == ThemeMode.dark ? RightBarTheme.darkRightBarTheme : RightBarTheme.lightRightBarTheme,
|
||||
contentTheme: ContentTheme.withColorTheme(colorTheme, mode: themeMode),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ class ThemeCustomizer {
|
||||
ThemeMode leftBarTheme = ThemeMode.light;
|
||||
ThemeMode rightBarTheme = ThemeMode.light;
|
||||
ThemeMode topBarTheme = ThemeMode.light;
|
||||
|
||||
ColorThemeType colorTheme = ColorThemeType.red;
|
||||
bool rightBarOpen = false;
|
||||
bool leftBarCondensed = false;
|
||||
|
||||
@ -73,6 +73,11 @@ class ThemeCustomizer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Public method to trigger theme updates externally
|
||||
static void applyThemeChange() {
|
||||
_notify();
|
||||
}
|
||||
|
||||
static void notify() {
|
||||
for (var value in _notifier) {
|
||||
value(oldInstance, instance);
|
||||
|
||||
271
lib/helpers/theme/theme_editor_widget.dart
Normal file
271
lib/helpers/theme/theme_editor_widget.dart
Normal file
@ -0,0 +1,271 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/helpers/widgets/wave_background.dart';
|
||||
import 'package:marco/helpers/theme/admin_theme.dart';
|
||||
import 'package:marco/helpers/theme/theme_customizer.dart';
|
||||
|
||||
class ThemeOption {
|
||||
final String label;
|
||||
final Color primary;
|
||||
final Color button;
|
||||
final Color brand;
|
||||
final ColorThemeType colorThemeType;
|
||||
|
||||
ThemeOption(
|
||||
this.label, this.primary, this.button, this.brand, this.colorThemeType);
|
||||
}
|
||||
|
||||
final List<ThemeOption> themeOptions = [
|
||||
ThemeOption(
|
||||
"Theme 1", Colors.red, Colors.red, Colors.red, ColorThemeType.red),
|
||||
ThemeOption(
|
||||
"Theme 2",
|
||||
const Color(0xFF49BF3C),
|
||||
const Color(0xFF49BF3C),
|
||||
const Color(0xFF49BF3C),
|
||||
ColorThemeType.green,
|
||||
),
|
||||
ThemeOption(
|
||||
"Theme 3",
|
||||
const Color(0xFF3F51B5),
|
||||
const Color(0xFF3F51B5),
|
||||
const Color(0xFF3F51B5),
|
||||
ColorThemeType.blue,
|
||||
),
|
||||
ThemeOption(
|
||||
"Theme 4",
|
||||
const Color(0xFF663399),
|
||||
const Color(0xFF663399),
|
||||
const Color(0xFF663399),
|
||||
ColorThemeType.purple,
|
||||
),
|
||||
];
|
||||
|
||||
class ThemeController extends GetxController {
|
||||
RxInt selectedIndex = 0.obs;
|
||||
RxBool showApplied = false.obs;
|
||||
|
||||
void init() {
|
||||
final currentPrimary = AdminTheme.theme.contentTheme.primary;
|
||||
int index = themeOptions
|
||||
.indexWhere((opt) => opt.primary.value == currentPrimary.value);
|
||||
selectedIndex.value = index == -1 ? 0 : index;
|
||||
}
|
||||
|
||||
void applyTheme(int index) async {
|
||||
selectedIndex.value = index;
|
||||
showApplied.value = true;
|
||||
|
||||
ThemeCustomizer.instance.colorTheme = themeOptions[index].colorThemeType;
|
||||
|
||||
ThemeCustomizer.applyThemeChange();
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 600));
|
||||
showApplied.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeEditorWidget extends StatefulWidget {
|
||||
final VoidCallback onClose;
|
||||
|
||||
const ThemeEditorWidget({super.key, required this.onClose});
|
||||
|
||||
@override
|
||||
_ThemeEditorWidgetState createState() => _ThemeEditorWidgetState();
|
||||
}
|
||||
|
||||
class _ThemeEditorWidgetState extends State<ThemeEditorWidget> {
|
||||
final ThemeController themeController = Get.put(ThemeController());
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
themeController.init();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header row with title and close button
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
MyText.bodyLarge("Theme Customization", fontWeight: 600),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: widget.onClose,
|
||||
tooltip: "Back",
|
||||
iconSize: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Theme cards wrapped in reactive Obx widget
|
||||
Center(
|
||||
child: Obx(
|
||||
() => Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 12,
|
||||
alignment: WrapAlignment.center,
|
||||
children: List.generate(themeOptions.length, (i) {
|
||||
return ThemeCard(
|
||||
themeOption: themeOptions[i],
|
||||
isSelected: themeController.selectedIndex.value == i,
|
||||
onTap: () => themeController.applyTheme(i),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Applied indicator reactive widget
|
||||
Obx(
|
||||
() => themeController.showApplied.value
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.check_circle,
|
||||
color:
|
||||
themeOptions[themeController.selectedIndex.value]
|
||||
.brand,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
"Theme Applied!",
|
||||
style: TextStyle(
|
||||
color: themeOptions[
|
||||
themeController.selectedIndex.value]
|
||||
.brand,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
"Preview and select a theme. You can change this anytime.",
|
||||
style: TextStyle(fontSize: 13, color: Colors.black54),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeCard extends StatelessWidget {
|
||||
final ThemeOption themeOption;
|
||||
final bool isSelected;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const ThemeCard({
|
||||
Key? key,
|
||||
required this.themeOption,
|
||||
required this.isSelected,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 80,
|
||||
child: Material(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
elevation: isSelected ? 4 : 1,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: onTap,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.easeInOut,
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: isSelected ? themeOption.brand : Colors.transparent,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 80,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
CustomPaint(
|
||||
painter: RedWavePainter(themeOption.brand, 0.15)),
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Hello, User!",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: themeOption.primary,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
SizedBox(
|
||||
height: 18,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: themeOption.button,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
elevation: 1,
|
||||
textStyle: const TextStyle(fontSize: 10),
|
||||
),
|
||||
onPressed: () {},
|
||||
child: const Text("Welcome"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
themeOption.label,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 12,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,38 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class WaveBackground extends StatelessWidget {
|
||||
final Color color;
|
||||
class RedWaveBackground extends StatelessWidget {
|
||||
final Color brandRed;
|
||||
final double heightFactor;
|
||||
|
||||
const WaveBackground({
|
||||
const RedWaveBackground({
|
||||
super.key,
|
||||
required this.color,
|
||||
this.heightFactor = 0.2,
|
||||
required this.brandRed,
|
||||
this.heightFactor = 0.2,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomPaint(
|
||||
painter: _WavePainter(color, heightFactor),
|
||||
painter: RedWavePainter(brandRed, heightFactor),
|
||||
size: Size.infinite,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _WavePainter extends CustomPainter {
|
||||
final Color color;
|
||||
class RedWavePainter extends CustomPainter {
|
||||
final Color brandRed;
|
||||
final double heightFactor;
|
||||
|
||||
_WavePainter(this.color, this.heightFactor);
|
||||
RedWavePainter(this.brandRed, this.heightFactor);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint1 = Paint()
|
||||
..shader = LinearGradient(
|
||||
colors: [
|
||||
const Color(0xFF49BF3C),
|
||||
const Color(0xFF81C784),
|
||||
],
|
||||
colors: [brandRed, const Color.fromARGB(255, 97, 22, 22)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
|
||||
@ -40,20 +37,26 @@ class _WavePainter extends CustomPainter {
|
||||
final path1 = Path()
|
||||
..moveTo(0, size.height * heightFactor)
|
||||
..quadraticBezierTo(
|
||||
size.width * 0.25, size.height * 0.05, size.width * 0.5, size.height * 0.15)
|
||||
size.width * 0.25, size.height * 0.05,
|
||||
size.width * 0.5, size.height * 0.15,
|
||||
)
|
||||
..quadraticBezierTo(
|
||||
size.width * 0.75, size.height * 0.25, size.width, size.height * 0.1)
|
||||
size.width * 0.75, size.height * 0.25,
|
||||
size.width, size.height * 0.1,
|
||||
)
|
||||
..lineTo(size.width, 0)
|
||||
..lineTo(0, 0)
|
||||
..close();
|
||||
|
||||
canvas.drawPath(path1, paint1);
|
||||
|
||||
// Secondary wave (overlay) with same green but lighter opacity
|
||||
final paint2 = Paint()..color = const Color(0xFF49BF3C).withOpacity(0.15);
|
||||
final paint2 = Paint()..color = brandRed.withOpacity(0.15);
|
||||
final path2 = Path()
|
||||
..moveTo(0, size.height * (heightFactor + 0.05))
|
||||
..quadraticBezierTo(size.width * 0.4, size.height * 0.1, size.width, size.height * 0.2)
|
||||
..quadraticBezierTo(
|
||||
size.width * 0.4, size.height * 0.1,
|
||||
size.width, size.height * 0.2,
|
||||
)
|
||||
..lineTo(size.width, 0)
|
||||
..lineTo(0, 0)
|
||||
..close();
|
||||
|
||||
@ -60,7 +60,7 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen>
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
WaveBackground(color: contentTheme.brandRed),
|
||||
RedWaveBackground(brandRed: contentTheme.primary),
|
||||
SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
@ -254,7 +254,7 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen>
|
||||
Widget _buildBackButton() {
|
||||
return TextButton.icon(
|
||||
onPressed: () async => await LocalStorage.logout(),
|
||||
icon: Icon(Icons.arrow_back, size: 18, color: contentTheme.primary),
|
||||
icon: Icon(Icons.arrow_back, size: 18, color: contentTheme.primary,),
|
||||
label: MyText.bodyMedium(
|
||||
'Back to Login',
|
||||
color: contentTheme.primary,
|
||||
|
||||
@ -56,7 +56,7 @@ class _WelcomeScreenState extends State<WelcomeScreen>
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => Dialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
insetPadding: const EdgeInsets.all(24),
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
@ -102,7 +102,7 @@ class _WelcomeScreenState extends State<WelcomeScreen>
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
WaveBackground(color: contentTheme.brandRed),
|
||||
RedWaveBackground(brandRed: contentTheme.primary),
|
||||
SafeArea(
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
@ -204,7 +204,7 @@ class _WelcomeScreenState extends State<WelcomeScreen>
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orangeAccent,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: MyText(
|
||||
'BETA',
|
||||
@ -235,9 +235,10 @@ class _WelcomeScreenState extends State<WelcomeScreen>
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: contentTheme.brandGreen,
|
||||
backgroundColor: contentTheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
elevation: 4,
|
||||
shadowColor: Colors.black26,
|
||||
),
|
||||
|
||||
@ -193,7 +193,7 @@ class _LoginScreenState extends State<LoginScreen> with UIMixin {
|
||||
elevation: 2,
|
||||
padding: MySpacing.xy(24, 16),
|
||||
borderRadiusAll: 5,
|
||||
backgroundColor:contentTheme.brandGreen,
|
||||
backgroundColor:contentTheme.primary,
|
||||
child: MyText.labelMedium(
|
||||
'Login',
|
||||
fontWeight: 600,
|
||||
|
||||
@ -10,6 +10,7 @@ import 'package:marco/helpers/services/api_endpoints.dart';
|
||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||
import 'package:marco/helpers/widgets/wave_background.dart';
|
||||
|
||||
|
||||
class MPINAuthScreen extends StatefulWidget {
|
||||
const MPINAuthScreen({super.key});
|
||||
|
||||
@ -52,7 +53,7 @@ class _MPINAuthScreenState extends State<MPINAuthScreen>
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
WaveBackground(color: contentTheme.brandRed),
|
||||
RedWaveBackground(brandRed: contentTheme.primary),
|
||||
SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
@ -111,7 +112,7 @@ class _MPINAuthScreenState extends State<MPINAuthScreen>
|
||||
horizontal: 10, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orangeAccent,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: MyText(
|
||||
'BETA',
|
||||
@ -146,7 +147,7 @@ class _MPINAuthScreenState extends State<MPINAuthScreen>
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black12,
|
||||
@ -265,7 +266,7 @@ class _MPINAuthScreenState extends State<MPINAuthScreen>
|
||||
filled: true,
|
||||
fillColor: Colors.grey.shade100,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
),
|
||||
@ -280,7 +281,7 @@ class _MPINAuthScreenState extends State<MPINAuthScreen>
|
||||
onPressed: controller.isLoading.value ? null : controller.onSubmitMPIN,
|
||||
elevation: 2,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
||||
borderRadiusAll: 5,
|
||||
borderRadiusAll: 10,
|
||||
backgroundColor: controller.isLoading.value
|
||||
? contentTheme.primary.withOpacity(0.6)
|
||||
: contentTheme.primary,
|
||||
@ -316,11 +317,11 @@ class _MPINAuthScreenState extends State<MPINAuthScreen>
|
||||
if (isNewUser || isChangeMpin)
|
||||
TextButton.icon(
|
||||
onPressed: () => Get.toNamed('/dashboard'),
|
||||
icon: const Icon(Icons.arrow_back,
|
||||
size: 18, color: Colors.redAccent),
|
||||
icon: Icon(Icons.arrow_back,
|
||||
size: 18, color: contentTheme.primary),
|
||||
label: MyText.bodyMedium(
|
||||
'Back to Home Page',
|
||||
color: contentTheme.brandRed,
|
||||
color: contentTheme.primary,
|
||||
fontWeight: 600,
|
||||
fontSize: 14,
|
||||
),
|
||||
@ -332,7 +333,7 @@ class _MPINAuthScreenState extends State<MPINAuthScreen>
|
||||
size: 18, color: Colors.redAccent),
|
||||
label: MyText.bodyMedium(
|
||||
'Go back to Login Screen',
|
||||
color: contentTheme.brandRed,
|
||||
color: contentTheme.primary,
|
||||
fontWeight: 600,
|
||||
fontSize: 14,
|
||||
),
|
||||
|
||||
@ -170,7 +170,7 @@ class _OTPLoginScreenState extends State<OTPLoginScreen> with UIMixin {
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: contentTheme.brandRed, width: 2),
|
||||
borderSide: BorderSide(color: contentTheme.primary, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -220,7 +220,7 @@ class _OTPLoginScreenState extends State<OTPLoginScreen> with UIMixin {
|
||||
elevation: 2,
|
||||
padding: MySpacing.xy(24, 16),
|
||||
borderRadiusAll: 10,
|
||||
backgroundColor: contentTheme.brandRed,
|
||||
backgroundColor: contentTheme.primary,
|
||||
child: MyText.labelMedium(
|
||||
'Verify OTP',
|
||||
fontWeight: 600,
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:marco/helpers/services/auth_service.dart';
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
import 'package:marco/helpers/widgets/my_spacing.dart';
|
||||
|
||||
class OrganizationFormBottomSheet {
|
||||
static void show(BuildContext context) {
|
||||
@ -81,169 +82,203 @@ class _OrganizationFormState extends State<_OrganizationForm> with UIMixin {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 40),
|
||||
child: SingleChildScrollView(
|
||||
controller: widget.scrollController,
|
||||
child: Form(
|
||||
key: validator.formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Center(
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 5,
|
||||
margin: const EdgeInsets.only(bottom: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
MyText.titleLarge(
|
||||
'Adventure starts here 🚀',
|
||||
fontWeight: 600,
|
||||
color: Colors.black87,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
MyText.bodySmall(
|
||||
"Make your app management easy and fun!",
|
||||
color: Colors.grey,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_sectionHeader('Organization Info'),
|
||||
_buildTextField('organizationName', 'Organization Name'),
|
||||
_buildTextField('email', 'Email',
|
||||
keyboardType: TextInputType.emailAddress),
|
||||
_buildTextField('about', 'About Organization'),
|
||||
_sectionHeader('Contact Details'),
|
||||
_buildTextField('contactPerson', 'Contact Person'),
|
||||
_buildTextField('contactNumber', 'Contact Number',
|
||||
keyboardType: TextInputType.phone),
|
||||
_buildTextField('address', 'Current Address'),
|
||||
_sectionHeader('Additional Details'),
|
||||
_buildPopupMenuField(
|
||||
'Organization Size',
|
||||
_sizes,
|
||||
_selectedSize,
|
||||
(val) => setState(() => _selectedSize = val),
|
||||
'Please select organization size',
|
||||
),
|
||||
_buildPopupMenuField(
|
||||
'Industry',
|
||||
_industries.map((e) => e['name'] as String).toList(),
|
||||
_selectedIndustryId != null
|
||||
? _industries.firstWhere(
|
||||
(e) => e['id'] == _selectedIndustryId)['name']
|
||||
: null,
|
||||
(val) {
|
||||
setState(() {
|
||||
final selectedIndustry = _industries.firstWhere(
|
||||
(element) => element['name'] == val,
|
||||
orElse: () => {},
|
||||
);
|
||||
_selectedIndustryId = selectedIndustry['id'];
|
||||
});
|
||||
},
|
||||
'Please select industry',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: _agreed,
|
||||
onChanged: (val) => setState(() => _agreed = val ?? false),
|
||||
fillColor: MaterialStateProperty.resolveWith((states) =>
|
||||
states.contains(MaterialState.selected)
|
||||
? contentTheme.primary
|
||||
: Colors.white),
|
||||
checkColor: Colors.white,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
MyText(
|
||||
'I agree to the ',
|
||||
color: Colors.black87,
|
||||
),
|
||||
MyText(
|
||||
'privacy policy & terms',
|
||||
color: contentTheme.primary,
|
||||
fontWeight: 600,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon: const Icon(Icons.close, color: Colors.white),
|
||||
label: MyText.bodyMedium(
|
||||
"Cancel",
|
||||
color: Colors.white,
|
||||
fontWeight: 600,
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _loading ? null : _submitForm,
|
||||
icon:
|
||||
Icon(Icons.check_circle_outline, color: Colors.white),
|
||||
label: MyText.bodyMedium(
|
||||
_loading ? "Submitting..." : "Submit",
|
||||
color: Colors.white,
|
||||
fontWeight: 600,
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: contentTheme
|
||||
.primary,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon:
|
||||
Icon(Icons.arrow_back, size: 18, color: contentTheme.primary),
|
||||
label: MyText.bodySmall(
|
||||
'Back to log in',
|
||||
fontWeight: 600,
|
||||
color: contentTheme.primary,
|
||||
),
|
||||
),
|
||||
return SingleChildScrollView(
|
||||
padding: MediaQuery.of(context).viewInsets,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 60),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black12,
|
||||
blurRadius: 12,
|
||||
offset: Offset(0, -2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 16, 20, 32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Center(
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 5,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade300,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
MySpacing.height(12),
|
||||
Center(
|
||||
child: MyText.titleLarge(
|
||||
'Adventure starts here 🚀',
|
||||
fontWeight: 700,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
MySpacing.height(4),
|
||||
Center(
|
||||
child: MyText.bodySmall(
|
||||
"Make your app management easy and fun!",
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
MySpacing.height(12),
|
||||
Form(
|
||||
key: validator.formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_sectionHeader('Organization Info'),
|
||||
_buildTextField('organizationName', 'Organization Name'),
|
||||
_buildTextField('email', 'Email',
|
||||
keyboardType: TextInputType.emailAddress),
|
||||
_buildTextField('about', 'About Organization'),
|
||||
_sectionHeader('Contact Details'),
|
||||
_buildTextField('contactPerson', 'Contact Person'),
|
||||
_buildTextField('contactNumber', 'Contact Number',
|
||||
keyboardType: TextInputType.phone),
|
||||
_buildTextField('address', 'Current Address'),
|
||||
_sectionHeader('Additional Details'),
|
||||
_buildPopupMenuField(
|
||||
'Organization Size',
|
||||
_sizes,
|
||||
_selectedSize,
|
||||
(val) => setState(() => _selectedSize = val),
|
||||
'Please select organization size',
|
||||
),
|
||||
_buildPopupMenuField(
|
||||
'Industry',
|
||||
_industries.map((e) => e['name'] as String).toList(),
|
||||
_selectedIndustryId != null
|
||||
? _industries.firstWhere(
|
||||
(e) => e['id'] == _selectedIndustryId)['name']
|
||||
: null,
|
||||
(val) {
|
||||
setState(() {
|
||||
final selectedIndustry = _industries.firstWhere(
|
||||
(element) => element['name'] == val,
|
||||
orElse: () => {},
|
||||
);
|
||||
_selectedIndustryId = selectedIndustry['id'];
|
||||
});
|
||||
},
|
||||
'Please select industry',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: _agreed,
|
||||
onChanged: (val) =>
|
||||
setState(() => _agreed = val ?? false),
|
||||
fillColor: MaterialStateProperty.resolveWith(
|
||||
(states) => states.contains(MaterialState.selected)
|
||||
? contentTheme.primary
|
||||
: Colors.white),
|
||||
checkColor: Colors.white,
|
||||
side: BorderSide(color: contentTheme.primary, width: 2),
|
||||
),
|
||||
Flexible(
|
||||
child: Wrap(
|
||||
children: [
|
||||
MyText(
|
||||
'I agree to the ',
|
||||
color: Colors.black87,
|
||||
),
|
||||
MyText(
|
||||
'privacy policy & terms',
|
||||
color: contentTheme.primary,
|
||||
fontWeight: 600,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
MySpacing.height(12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon: const Icon(Icons.close, color: Colors.white),
|
||||
label: MyText.bodyMedium(
|
||||
"Cancel",
|
||||
color: Colors.white,
|
||||
fontWeight: 600,
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _loading ? null : _submitForm,
|
||||
icon: _loading
|
||||
? SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: const Icon(Icons.check_circle_outline,
|
||||
color: Colors.white),
|
||||
label: MyText.bodyMedium(
|
||||
_loading ? "Submitting..." : "Submit",
|
||||
color: Colors.white,
|
||||
fontWeight: 600,
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: contentTheme.primary,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
MySpacing.height(12),
|
||||
Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon: Icon(
|
||||
Icons.arrow_back,
|
||||
size: 18,
|
||||
color: contentTheme.primary,
|
||||
),
|
||||
label: MyText.bodySmall(
|
||||
'Back to log in',
|
||||
fontWeight: 600,
|
||||
color: contentTheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -290,19 +325,19 @@ class _OrganizationFormState extends State<_OrganizationForm> with UIMixin {
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.grey[300]!),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(color: contentTheme.brandRed, width: 1.5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: contentTheme.primary, width: 1.5),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: Colors.red),
|
||||
),
|
||||
),
|
||||
@ -366,17 +401,17 @@ class _OrganizationFormState extends State<_OrganizationForm> with UIMixin {
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16, vertical: 16),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.grey[300]!),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide:
|
||||
BorderSide(color: contentTheme.brandRed, width: 1.5),
|
||||
BorderSide(color: contentTheme.primary, width: 1.5),
|
||||
),
|
||||
errorText: fieldState.errorText,
|
||||
),
|
||||
@ -385,8 +420,7 @@ class _OrganizationFormState extends State<_OrganizationForm> with UIMixin {
|
||||
children: [
|
||||
MyText.bodyMedium(
|
||||
selectedValue ?? 'Select $label',
|
||||
color:
|
||||
selectedValue == null ? Colors.grey : Colors.black,
|
||||
color: selectedValue == null ? Colors.grey : Colors.black,
|
||||
),
|
||||
const Icon(Icons.arrow_drop_down, color: Colors.grey),
|
||||
],
|
||||
|
||||
@ -134,7 +134,7 @@ class _NotesViewState extends State<NotesView> with UIMixin {
|
||||
),
|
||||
if (note.isActive) ...[
|
||||
IconButton(
|
||||
icon: Icon(isEditing ? Icons.close : Icons.edit_outlined,
|
||||
icon: Icon(isEditing ? Icons.close : Icons.edit ,
|
||||
color: Colors.indigo, size: 18),
|
||||
splashRadius: 18,
|
||||
onPressed: () {
|
||||
@ -143,7 +143,7 @@ class _NotesViewState extends State<NotesView> with UIMixin {
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete_outline,
|
||||
icon: const Icon(Icons.delete,
|
||||
size: 18, color: Colors.red),
|
||||
splashRadius: 18,
|
||||
onPressed: () async {
|
||||
@ -216,7 +216,7 @@ class _NotesViewState extends State<NotesView> with UIMixin {
|
||||
await controller.updateNote(updated);
|
||||
controller.editingNoteId.value = null;
|
||||
},
|
||||
icon: const Icon(Icons.check_circle_outline,
|
||||
icon: const Icon(Icons.check_circle,
|
||||
color: Colors.white),
|
||||
label: MyText.bodyMedium(
|
||||
"Save",
|
||||
|
||||
@ -9,6 +9,7 @@ import 'package:marco/helpers/services/api_endpoints.dart';
|
||||
import 'package:marco/images.dart';
|
||||
import 'package:marco/controller/project_controller.dart';
|
||||
import 'package:marco/view/layouts/user_profile_right_bar.dart';
|
||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||
|
||||
class Layout extends StatefulWidget {
|
||||
final Widget? child;
|
||||
@ -20,7 +21,7 @@ class Layout extends StatefulWidget {
|
||||
State<Layout> createState() => _LayoutState();
|
||||
}
|
||||
|
||||
class _LayoutState extends State<Layout> {
|
||||
class _LayoutState extends State<Layout> with UIMixin {
|
||||
final LayoutController controller = LayoutController();
|
||||
final EmployeeInfo? employeeInfo = LocalStorage.getEmployeeInfo();
|
||||
final bool isBetaEnvironment = ApiEndpoints.baseUrl.contains("stage");
|
||||
@ -409,13 +410,13 @@ class _LayoutState extends State<Layout> {
|
||||
style: TextStyle(
|
||||
fontWeight:
|
||||
isSelected ? FontWeight.bold : FontWeight.normal,
|
||||
color: isSelected ? Colors.blueAccent : Colors.black87,
|
||||
color: isSelected ? contentTheme.primary : Colors.black87,
|
||||
),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
activeColor: Colors.blueAccent,
|
||||
activeColor: contentTheme.primary,
|
||||
tileColor: isSelected
|
||||
? Colors.blueAccent.withOpacity(0.1)
|
||||
? contentTheme.primary.withOpacity(0.1)
|
||||
: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
|
||||
@ -39,7 +39,7 @@ class _OfflineScreenState extends State<OfflineScreen>
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
_RedWaveBackground(brandRed: contentTheme.brandRed),
|
||||
_RedWaveBackground(primary: contentTheme.primary),
|
||||
SafeArea(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
@ -101,28 +101,28 @@ class _OfflineScreenState extends State<OfflineScreen>
|
||||
}
|
||||
|
||||
class _RedWaveBackground extends StatelessWidget {
|
||||
final Color brandRed;
|
||||
const _RedWaveBackground({required this.brandRed});
|
||||
final Color primary;
|
||||
const _RedWaveBackground({required this.primary});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomPaint(
|
||||
painter: _WavePainter(brandRed),
|
||||
painter: _WavePainter(primary),
|
||||
size: Size.infinite,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _WavePainter extends CustomPainter {
|
||||
final Color brandRed;
|
||||
final Color primary;
|
||||
|
||||
_WavePainter(this.brandRed);
|
||||
_WavePainter(this.primary);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint1 = Paint()
|
||||
..shader = LinearGradient(
|
||||
colors: [brandRed, const Color.fromARGB(255, 97, 22, 22)],
|
||||
colors: [primary, const Color.fromARGB(255, 97, 22, 22)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
|
||||
|
||||
@ -10,8 +10,8 @@ import 'package:marco/helpers/widgets/avatar.dart';
|
||||
import 'package:marco/model/employees/employee_info.dart';
|
||||
import 'package:marco/controller/auth/mpin_controller.dart';
|
||||
import 'package:marco/view/employees/employee_profile_screen.dart';
|
||||
import 'package:marco/view/support/support_screen.dart';
|
||||
import 'package:marco/view/faq/faq_screen.dart';
|
||||
import 'package:marco/helpers/theme/theme_editor_widget.dart';
|
||||
|
||||
|
||||
class UserProfileBar extends StatefulWidget {
|
||||
final bool isCondensed;
|
||||
@ -26,6 +26,7 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
late EmployeeInfo employeeInfo;
|
||||
bool _isLoading = true;
|
||||
bool hasMpin = true;
|
||||
bool _isThemeEditorVisible = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -45,7 +46,7 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 14),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(22),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 18, sigmaY: 18),
|
||||
child: AnimatedContainer(
|
||||
@ -55,59 +56,72 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Colors.white.withValues(alpha: 0.95),
|
||||
Colors.white.withValues(alpha: 0.85),
|
||||
Colors.white.withOpacity(0.95),
|
||||
Colors.white.withOpacity(0.85),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(22),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.06),
|
||||
color: Colors.black.withOpacity(0.06),
|
||||
blurRadius: 18,
|
||||
offset: const Offset(0, 8),
|
||||
)
|
||||
],
|
||||
border: Border.all(
|
||||
color: Colors.grey.withValues(alpha: 0.25),
|
||||
color: Colors.grey.withOpacity(0.25),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
bottom: true,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_isLoading
|
||||
? const _LoadingSection()
|
||||
: _userProfileSection(isCondensed),
|
||||
MySpacing.height(12),
|
||||
Divider(
|
||||
indent: 18,
|
||||
endIndent: 18,
|
||||
thickness: 0.7,
|
||||
color: Colors.grey.withValues(alpha: 0.25),
|
||||
),
|
||||
MySpacing.height(12),
|
||||
_supportAndSettingsMenu(isCondensed),
|
||||
const Spacer(),
|
||||
Divider(
|
||||
indent: 18,
|
||||
endIndent: 18,
|
||||
thickness: 0.35,
|
||||
color: Colors.grey.withValues(alpha: 0.18),
|
||||
),
|
||||
_logoutButton(isCondensed),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottom: true,
|
||||
child: Stack(
|
||||
children: [
|
||||
Offstage(
|
||||
offstage: _isThemeEditorVisible,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_isLoading
|
||||
? const _LoadingSection()
|
||||
: _userProfileSection(isCondensed),
|
||||
MySpacing.height(12),
|
||||
Divider(
|
||||
indent: 18,
|
||||
endIndent: 18,
|
||||
thickness: 0.7,
|
||||
color: Colors.grey.withOpacity(0.25),
|
||||
),
|
||||
MySpacing.height(12),
|
||||
_supportAndSettingsMenu(isCondensed),
|
||||
const Spacer(),
|
||||
Divider(
|
||||
indent: 18,
|
||||
endIndent: 18,
|
||||
thickness: 0.35,
|
||||
color: Colors.grey.withOpacity(0.18),
|
||||
),
|
||||
_logoutButton(isCondensed),
|
||||
],
|
||||
),
|
||||
),
|
||||
Offstage(
|
||||
offstage: !_isThemeEditorVisible,
|
||||
child: ThemeEditorWidget(
|
||||
onClose: () {
|
||||
setState(() => _isThemeEditorVisible = false);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _userProfileSection(bool condensed) {
|
||||
final padding = MySpacing.fromLTRB(
|
||||
condensed ? 16 : 26,
|
||||
@ -126,7 +140,7 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).primaryColor.withValues(alpha: 0.15),
|
||||
color: Theme.of(context).primaryColor.withOpacity(0.15),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 1,
|
||||
),
|
||||
@ -180,22 +194,23 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
),
|
||||
SizedBox(height: spacingHeight),
|
||||
_menuItemRow(
|
||||
icon: LucideIcons.badge_help,
|
||||
label: 'Support',
|
||||
onTap: _onSupportTap,
|
||||
icon: LucideIcons.settings,
|
||||
label: 'Settings',
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isThemeEditorVisible = true;
|
||||
});
|
||||
},
|
||||
),
|
||||
SizedBox(height: spacingHeight),
|
||||
_menuItemRow(
|
||||
icon: LucideIcons.info,
|
||||
label: 'FAQ', // <-- New FAQ menu item
|
||||
onTap: _onFaqTap, // <-- Handle tap
|
||||
icon: LucideIcons.badge_alert,
|
||||
label: 'Support',
|
||||
),
|
||||
SizedBox(height: spacingHeight),
|
||||
_menuItemRow(
|
||||
icon: LucideIcons.lock,
|
||||
label: hasMpin ? 'Change MPIN' : 'Set MPIN',
|
||||
iconColor: contentTheme.primary,
|
||||
textColor: contentTheme.primary,
|
||||
onTap: _onMpinTap,
|
||||
),
|
||||
],
|
||||
@ -203,14 +218,6 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
);
|
||||
}
|
||||
|
||||
void _onFaqTap() {
|
||||
Get.to(() => const FAQScreen());
|
||||
}
|
||||
|
||||
void _onSupportTap() {
|
||||
Get.to(() => const SupportScreen());
|
||||
}
|
||||
|
||||
Widget _menuItemRow({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
@ -220,12 +227,12 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.2), width: 1),
|
||||
),
|
||||
child: Row(
|
||||
@ -277,15 +284,15 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
fontWeight: 700,
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red.shade600,
|
||||
backgroundColor: contentTheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
shadowColor: Colors.red.shade200,
|
||||
shadowColor: contentTheme.primary,
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: condensed ? 9 : 12,
|
||||
horizontal: condensed ? 6 : 16,
|
||||
vertical: condensed ? 14 : 18,
|
||||
horizontal: condensed ? 14 : 22,
|
||||
),
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -301,54 +308,71 @@ class _UserProfileBarState extends State<UserProfileBar>
|
||||
}
|
||||
|
||||
Widget _buildLogoutDialog(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final primaryColor = contentTheme.primary;
|
||||
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
|
||||
elevation: 10,
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: theme.cardColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 34),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(LucideIcons.log_out, size: 56, color: Colors.red.shade700),
|
||||
const SizedBox(height: 18),
|
||||
const Text(
|
||||
// Top icon
|
||||
Icon(LucideIcons.log_out, size: 56, color: primaryColor),
|
||||
MySpacing.height(18),
|
||||
// Title
|
||||
MyText.titleLarge(
|
||||
"Logout Confirmation",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.black87),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
const Text(
|
||||
"Are you sure you want to logout?\nYou will need to login again to continue.",
|
||||
fontWeight: 700,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 16, color: Colors.black54),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
MySpacing.height(14),
|
||||
// Subtitle
|
||||
MyText.bodyMedium(
|
||||
"Are you sure you want to logout?\nYou will need to login again to continue.",
|
||||
color: Colors.grey[700],
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
MySpacing.height(30),
|
||||
// Buttons
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.grey.shade700,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12)),
|
||||
child: const Text("Cancel"),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14)),
|
||||
),
|
||||
child: MyText.bodyMedium(
|
||||
"Cancel",
|
||||
color: Colors.white,
|
||||
fontWeight: 600,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 18),
|
||||
MySpacing.width(18),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red.shade700,
|
||||
backgroundColor: primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5)),
|
||||
borderRadius: BorderRadius.circular(14)),
|
||||
),
|
||||
child: MyText.bodyMedium(
|
||||
"Logout",
|
||||
color: Colors.white,
|
||||
fontWeight: 600,
|
||||
),
|
||||
child: const Text("Logout"),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user