changed theme to green

This commit is contained in:
Vaibhav Surve 2025-10-09 14:50:54 +05:30
parent f281eb5b50
commit 8c3493b792
20 changed files with 328 additions and 349 deletions

View File

@ -19,14 +19,19 @@ enum ContentThemeColor {
pink,
green,
red,
brandRed;
brandRed,
brandGreen;
Color get color {
return (AdminTheme.theme.contentTheme.getMappedIntoThemeColor[this]?['color']) ?? Colors.black;
return (AdminTheme.theme.contentTheme.getMappedIntoThemeColor[this]
?['color']) ??
Colors.black;
}
Color get onColor {
return (AdminTheme.theme.contentTheme.getMappedIntoThemeColor[this]?['onColor']) ?? Colors.white;
return (AdminTheme.theme.contentTheme.getMappedIntoThemeColor[this]
?['onColor']) ??
Colors.white;
}
}
@ -77,7 +82,9 @@ class TopBarTheme {
static final TopBarTheme lightTopBarTheme = TopBarTheme();
static final TopBarTheme darkTopBarTheme = TopBarTheme(background: const Color(0xff2c3036), onBackground: const Color(0xffdcdcdc));
static final TopBarTheme darkTopBarTheme = TopBarTheme(
background: const Color(0xff2c3036),
onBackground: const Color(0xffdcdcdc));
}
class RightBarTheme {
@ -121,6 +128,7 @@ class ContentTheme {
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;
@ -130,7 +138,10 @@ class ContentTheme {
var c = AdminTheme.theme.contentTheme;
return {
ContentThemeColor.primary: {'color': c.primary, 'onColor': c.onPrimary},
ContentThemeColor.secondary: {'color': c.secondary, 'onColor': c.onSecondary},
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},
@ -139,14 +150,21 @@ class ContentTheme {
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.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(0xff663399),
this.primary = const Color(0xFF49BF3C),
this.onPrimary = const Color(0xffffffff),
this.secondary = const Color(0xff6c757d),
this.onSecondary = const Color(0xffffffff),
@ -178,12 +196,12 @@ 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),
});
//-------------------------------------- Left Bar Theme ----------------------------------------//
static final ContentTheme lightContentTheme = ContentTheme(
primary: Color(0xff663399),
primary: Color(0xFF49BF3C),
background: const Color(0xfffafbfe),
onBackground: const Color(0xff313a46),
cardBorder: const Color(0xffe8ecf1),
@ -197,7 +215,7 @@ class ContentTheme {
);
static final ContentTheme darkContentTheme = ContentTheme(
primary: Color(0xff32BFAE),
primary: Color(0xFF49BF3C),
background: const Color(0xff343a40),
onBackground: const Color(0xffF1F1F2),
disabled: const Color(0xff444d57),
@ -236,9 +254,17 @@ class AdminTheme {
static void setTheme() {
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: 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);
}
}

View File

@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
class BaseBottomSheet extends StatelessWidget {
class BaseBottomSheet extends StatefulWidget {
final String title;
final String? subtitle;
final Widget child;
@ -10,7 +11,7 @@ class BaseBottomSheet extends StatelessWidget {
final VoidCallback onSubmit;
final bool isSubmitting;
final String submitText;
final Color submitColor;
final Color? submitColor;
final IconData submitIcon;
final bool showButtons;
final Widget? bottomContent;
@ -24,16 +25,23 @@ class BaseBottomSheet extends StatelessWidget {
this.subtitle,
this.isSubmitting = false,
this.submitText = 'Submit',
this.submitColor = Colors.indigo,
this.submitColor,
this.submitIcon = Icons.check_circle_outline,
this.showButtons = true,
this.bottomContent,
});
@override
State<BaseBottomSheet> createState() => _BaseBottomSheetState();
}
class _BaseBottomSheetState extends State<BaseBottomSheet> with UIMixin {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final mediaQuery = MediaQuery.of(context);
final effectiveSubmitColor =
widget.submitColor ?? contentTheme.primary;
return SingleChildScrollView(
padding: mediaQuery.viewInsets,
@ -71,35 +79,31 @@ class BaseBottomSheet extends StatelessWidget {
),
),
MySpacing.height(12),
// Centered title
Center(
child: MyText.titleLarge(
title,
widget.title,
fontWeight: 700,
textAlign: TextAlign.center,
),
),
// Subtitle shown just below header if provided
if (subtitle != null && subtitle!.isNotEmpty) ...[
if (widget.subtitle != null &&
widget.subtitle!.isNotEmpty) ...[
MySpacing.height(4),
MyText.bodySmall(
subtitle!,
widget.subtitle!,
fontWeight: 600,
color: Colors.grey[700],
),
],
MySpacing.height(12),
child,
widget.child,
MySpacing.height(12),
if (showButtons) ...[
if (widget.showButtons) ...[
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: onCancel,
onPressed: widget.onCancel,
icon: const Icon(Icons.close, color: Colors.white),
label: MyText.bodyMedium(
"Cancel",
@ -111,34 +115,40 @@ class BaseBottomSheet extends StatelessWidget {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(vertical: 8),
padding:
const EdgeInsets.symmetric(vertical: 8),
),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
onPressed: isSubmitting ? null : onSubmit,
icon: Icon(submitIcon, color: Colors.white),
onPressed:
widget.isSubmitting ? null : widget.onSubmit,
icon:
Icon(widget.submitIcon, color: Colors.white),
label: MyText.bodyMedium(
isSubmitting ? "Submitting..." : submitText,
widget.isSubmitting
? "Submitting..."
: widget.submitText,
color: Colors.white,
fontWeight: 600,
),
style: ElevatedButton.styleFrom(
backgroundColor: submitColor,
backgroundColor: effectiveSubmitColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(vertical: 8),
padding:
const EdgeInsets.symmetric(vertical: 8),
),
),
),
],
),
if (bottomContent != null) ...[
if (widget.bottomContent != null) ...[
MySpacing.height(12),
bottomContent!,
widget.bottomContent!,
],
],
],

View File

@ -9,6 +9,7 @@ import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/model/expense/expense_list_model.dart';
import 'package:marco/view/expense/expense_detail_screen.dart';
import 'package:marco/helpers/widgets/my_confirmation_dialog.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
class ExpenseAppBar extends StatelessWidget implements PreferredSizeWidget {
final ProjectController projectController;
@ -72,7 +73,7 @@ class ExpenseAppBar extends StatelessWidget implements PreferredSizeWidget {
}
}
class SearchAndFilter extends StatelessWidget {
class SearchAndFilter extends StatefulWidget {
final TextEditingController controller;
final ValueChanged<String> onChanged;
final VoidCallback onFilterTap;
@ -86,6 +87,11 @@ class SearchAndFilter extends StatelessWidget {
super.key,
});
@override
State<SearchAndFilter> createState() => _SearchAndFilterState();
}
class _SearchAndFilterState extends State<SearchAndFilter> with UIMixin {
@override
Widget build(BuildContext context) {
return Padding(
@ -96,8 +102,8 @@ class SearchAndFilter extends StatelessWidget {
child: SizedBox(
height: 35,
child: TextField(
controller: controller,
onChanged: onChanged,
controller: widget.controller,
onChanged: widget.onChanged,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(horizontal: 12),
prefixIcon:
@ -109,6 +115,11 @@ class SearchAndFilter extends StatelessWidget {
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
borderSide:
BorderSide(color: contentTheme.primary, width: 1.5),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.grey.shade300),
@ -124,7 +135,7 @@ class SearchAndFilter extends StatelessWidget {
clipBehavior: Clip.none,
children: [
const Icon(Icons.tune, color: Colors.black),
if (expenseController.isFilterApplied)
if (widget.expenseController.isFilterApplied)
Positioned(
top: -1,
right: -1,
@ -140,7 +151,7 @@ class SearchAndFilter extends StatelessWidget {
),
],
),
onPressed: onFilterTap,
onPressed: widget.onFilterTap,
);
}),
],

View File

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
class WaveBackground extends StatelessWidget {
final Color color;
final double heightFactor;
const WaveBackground({
super.key,
required this.color,
this.heightFactor = 0.2,
});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _WavePainter(color, heightFactor),
size: Size.infinite,
);
}
}
class _WavePainter extends CustomPainter {
final Color color;
final double heightFactor;
_WavePainter(this.color, this.heightFactor);
@override
void paint(Canvas canvas, Size size) {
final paint1 = Paint()
..shader = LinearGradient(
colors: [
const Color(0xFF49BF3C),
const Color(0xFF81C784),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
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)
..quadraticBezierTo(
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 path2 = Path()
..moveTo(0, size.height * (heightFactor + 0.05))
..quadraticBezierTo(size.width * 0.4, size.height * 0.1, size.width, size.height * 0.2)
..lineTo(size.width, 0)
..lineTo(0, 0)
..close();
canvas.drawPath(path2, paint2);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

View File

@ -94,7 +94,7 @@ class _EmailLoginFormState extends State<EmailLoginForm> with UIMixin {
MaterialStateProperty.resolveWith<Color>(
(states) =>
states.contains(WidgetState.selected)
? contentTheme.brandRed
? contentTheme.primary
: Colors.white,
),
checkColor: contentTheme.onPrimary,
@ -128,7 +128,7 @@ class _EmailLoginFormState extends State<EmailLoginForm> with UIMixin {
elevation: 2,
padding: MySpacing.xy(80, 16),
borderRadiusAll: 10,
backgroundColor: contentTheme.brandRed,
backgroundColor: contentTheme.primary,
child: MyText.labelLarge(
'Login',
fontWeight: 700,

View File

@ -8,6 +8,7 @@ import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
import 'package:marco/helpers/widgets/my_button.dart';
import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/images.dart';
import 'package:marco/helpers/widgets/wave_background.dart';
class ForgotPasswordScreen extends StatefulWidget {
const ForgotPasswordScreen({super.key});
@ -59,7 +60,7 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen>
return Scaffold(
body: Stack(
children: [
_RedWaveBackground(brandRed: contentTheme.brandRed),
WaveBackground(color: contentTheme.brandRed),
SafeArea(
child: Center(
child: Column(
@ -230,8 +231,8 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen>
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 16),
borderRadiusAll: 10,
backgroundColor: _isLoading
? contentTheme.brandRed.withOpacity(0.6)
: contentTheme.brandRed,
? contentTheme.primary.withOpacity(0.6)
: contentTheme.primary,
child: _isLoading
? const SizedBox(
height: 20,
@ -253,68 +254,13 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen>
Widget _buildBackButton() {
return TextButton.icon(
onPressed: () async => await LocalStorage.logout(),
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 Login',
color: contentTheme.brandRed,
color: contentTheme.primary,
fontWeight: 600,
fontSize: 14,
),
);
}
}
class _RedWaveBackground extends StatelessWidget {
final Color brandRed;
const _RedWaveBackground({required this.brandRed});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _WavePainter(brandRed),
size: Size.infinite,
);
}
}
class _WavePainter extends CustomPainter {
final Color brandRed;
_WavePainter(this.brandRed);
@override
void paint(Canvas canvas, Size size) {
final paint1 = Paint()
..shader = LinearGradient(
colors: [brandRed, const Color.fromARGB(255, 97, 22, 22)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
final path1 = Path()
..moveTo(0, size.height * 0.2)
..quadraticBezierTo(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)
..lineTo(size.width, 0)
..lineTo(0, 0)
..close();
canvas.drawPath(path1, paint1);
final paint2 = Paint()..color = Colors.redAccent.withOpacity(0.15);
final path2 = Path()
..moveTo(0, size.height * 0.25)
..quadraticBezierTo(
size.width * 0.4, size.height * 0.1, size.width, size.height * 0.2)
..lineTo(size.width, 0)
..lineTo(0, 0)
..close();
canvas.drawPath(path2, paint2);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

View File

@ -7,6 +7,7 @@ import 'package:marco/view/auth/otp_login_form.dart';
import 'package:marco/helpers/services/api_endpoints.dart';
import 'package:marco/view/auth/request_demo_bottom_sheet.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
import 'package:marco/helpers/widgets/wave_background.dart';
enum LoginOption { email, otp }
@ -101,13 +102,14 @@ class _WelcomeScreenState extends State<WelcomeScreen>
return Scaffold(
body: Stack(
children: [
_RedWaveBackground(brandRed: contentTheme.brandRed),
WaveBackground(color: contentTheme.brandRed),
SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: isNarrow ? double.infinity : 420),
constraints: BoxConstraints(
maxWidth: isNarrow ? double.infinity : 420),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@ -166,7 +168,10 @@ class _WelcomeScreenState extends State<WelcomeScreen>
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 10, offset: Offset(0, 4))],
boxShadow: [
BoxShadow(
color: Colors.black12, blurRadius: 10, offset: Offset(0, 4))
],
),
child: Image.asset(Images.logoDark),
),
@ -230,7 +235,7 @@ class _WelcomeScreenState extends State<WelcomeScreen>
),
),
style: ElevatedButton.styleFrom(
backgroundColor: contentTheme.brandRed,
backgroundColor: contentTheme.brandGreen,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
elevation: 4,
@ -247,55 +252,3 @@ class _WelcomeScreenState extends State<WelcomeScreen>
);
}
}
// Red wave background painter
class _RedWaveBackground extends StatelessWidget {
final Color brandRed;
const _RedWaveBackground({required this.brandRed});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _WavePainter(brandRed),
size: Size.infinite,
);
}
}
class _WavePainter extends CustomPainter {
final Color brandRed;
_WavePainter(this.brandRed);
@override
void paint(Canvas canvas, Size size) {
final paint1 = Paint()
..shader = LinearGradient(
colors: [brandRed, const Color.fromARGB(255, 97, 22, 22)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
final path1 = Path()
..moveTo(0, size.height * 0.2)
..quadraticBezierTo(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)
..lineTo(size.width, 0)
..lineTo(0, 0)
..close();
canvas.drawPath(path1, paint1);
final paint2 = Paint()..color = Colors.redAccent.withOpacity(0.15);
final path2 = Path()
..moveTo(0, size.height * 0.25)
..quadraticBezierTo(size.width * 0.4, size.height * 0.1, size.width, size.height * 0.2)
..lineTo(size.width, 0)
..lineTo(0, 0)
..close();
canvas.drawPath(path2, paint2);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

View File

@ -193,7 +193,7 @@ class _LoginScreenState extends State<LoginScreen> with UIMixin {
elevation: 2,
padding: MySpacing.xy(24, 16),
borderRadiusAll: 5,
backgroundColor: Colors.blueAccent,
backgroundColor:contentTheme.brandGreen,
child: MyText.labelMedium(
'Login',
fontWeight: 600,

View File

@ -8,6 +8,7 @@ import 'package:marco/images.dart';
import 'package:marco/helpers/services/storage/local_storage.dart';
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});
@ -51,7 +52,7 @@ class _MPINAuthScreenState extends State<MPINAuthScreen>
return Scaffold(
body: Stack(
children: [
_RedWaveBackground(brandRed: contentTheme.brandRed),
WaveBackground(color: contentTheme.brandRed),
SafeArea(
child: Center(
child: Column(
@ -281,8 +282,8 @@ class _MPINAuthScreenState extends State<MPINAuthScreen>
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
borderRadiusAll: 5,
backgroundColor: controller.isLoading.value
? contentTheme.brandRed.withOpacity(0.6)
: contentTheme.brandRed,
? contentTheme.primary.withOpacity(0.6)
: contentTheme.primary,
child: controller.isLoading.value
? const SizedBox(
height: 20,
@ -341,57 +342,3 @@ class _MPINAuthScreenState extends State<MPINAuthScreen>
});
}
}
class _RedWaveBackground extends StatelessWidget {
final Color brandRed;
const _RedWaveBackground({required this.brandRed});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _WavePainter(brandRed),
size: Size.infinite,
);
}
}
class _WavePainter extends CustomPainter {
final Color brandRed;
const _WavePainter(this.brandRed);
@override
void paint(Canvas canvas, Size size) {
final paint1 = Paint()
..shader = LinearGradient(
colors: [brandRed, const Color.fromARGB(255, 97, 22, 22)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
final path1 = Path()
..moveTo(0, size.height * 0.2)
..quadraticBezierTo(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)
..lineTo(size.width, 0)
..lineTo(0, 0)
..close();
canvas.drawPath(path1, paint1);
final paint2 = Paint()..color = Colors.redAccent.withOpacity(0.15);
final path2 = Path()
..moveTo(0, size.height * 0.25)
..quadraticBezierTo(
size.width * 0.4, size.height * 0.1, size.width, size.height * 0.2)
..lineTo(size.width, 0)
..lineTo(0, 0)
..close();
canvas.drawPath(path2, paint2);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

View File

@ -81,7 +81,7 @@ class _OTPLoginScreenState extends State<OTPLoginScreen> with UIMixin {
elevation: 2,
padding: MySpacing.xy(24, 16),
borderRadiusAll: 10,
backgroundColor: isDisabled ? Colors.grey : contentTheme.brandRed,
backgroundColor: isDisabled ? Colors.grey : contentTheme.primary,
child: controller.isSending.value
? SizedBox(
width: 20,

View File

@ -166,10 +166,9 @@ class _OrganizationFormState extends State<_OrganizationForm> with UIMixin {
onChanged: (val) => setState(() => _agreed = val ?? false),
fillColor: MaterialStateProperty.resolveWith((states) =>
states.contains(MaterialState.selected)
? contentTheme.brandRed
? contentTheme.primary
: Colors.white),
checkColor: Colors.white,
side: const BorderSide(color: Colors.red, width: 2),
),
Row(
children: [
@ -179,7 +178,7 @@ class _OrganizationFormState extends State<_OrganizationForm> with UIMixin {
),
MyText(
'privacy policy & terms',
color: contentTheme.brandRed,
color: contentTheme.primary,
fontWeight: 600,
),
],
@ -188,44 +187,44 @@ class _OrganizationFormState extends State<_OrganizationForm> with UIMixin {
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
OutlinedButton.icon(
Expanded(
child: ElevatedButton.icon(
onPressed: () => Navigator.pop(context),
icon: const Icon(Icons.arrow_back, color: Colors.red),
label: MyText.bodyMedium("Back", color: Colors.red),
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.red),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
padding: const EdgeInsets.symmetric(
horizontal: 28, vertical: 5),
),
),
ElevatedButton.icon(
onPressed: _loading ? null : _submitForm,
icon: _loading
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
icon: const Icon(Icons.close, color: Colors.white),
label: MyText.bodyMedium(
"Cancel",
color: Colors.white,
strokeWidth: 2,
fontWeight: 600,
),
)
: const Icon(Icons.check_circle_outline,
color: Colors.white),
label: _loading
? const SizedBox.shrink()
: MyText.bodyMedium("Submit", color: Colors.white),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.indigo,
backgroundColor: Colors.grey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
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),
),
padding: const EdgeInsets.symmetric(
horizontal: 28, vertical: 5),
),
),
],
@ -235,11 +234,11 @@ class _OrganizationFormState extends State<_OrganizationForm> with UIMixin {
child: TextButton.icon(
onPressed: () => Navigator.pop(context),
icon:
const Icon(Icons.arrow_back, size: 18, color: Colors.red),
Icon(Icons.arrow_back, size: 18, color: contentTheme.primary),
label: MyText.bodySmall(
'Back to log in',
fontWeight: 600,
color: contentTheme.brandRed,
color: contentTheme.primary,
),
),
),

View File

@ -16,6 +16,7 @@ import 'package:marco/helpers/utils/date_time_utils.dart';
import 'package:marco/model/directory/add_contact_bottom_sheet.dart';
import 'package:marco/helpers/widgets/my_refresh_indicator.dart';
import 'package:marco/helpers/widgets/my_confirmation_dialog.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
// HELPER: Delta to HTML conversion
String _convertDeltaToHtml(dynamic delta) {
@ -68,7 +69,7 @@ class ContactDetailScreen extends StatefulWidget {
State<ContactDetailScreen> createState() => _ContactDetailScreenState();
}
class _ContactDetailScreenState extends State<ContactDetailScreen> {
class _ContactDetailScreenState extends State<ContactDetailScreen> with UIMixin{
late final DirectoryController directoryController;
late final ProjectController projectController;
@ -191,7 +192,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
TabBar(
labelColor: Colors.black,
unselectedLabelColor: Colors.grey,
indicatorColor: Colors.red,
indicatorColor: contentTheme.primary,
tabs: const [
Tab(text: "Details"),
Tab(text: "Notes"),
@ -308,7 +309,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
bottom: 20,
right: 20,
child: FloatingActionButton.extended(
backgroundColor: Colors.red,
backgroundColor: contentTheme.primary,
onPressed: () async {
final result = await Get.bottomSheet(
AddContactBottomSheet(existingContact: contact),
@ -383,7 +384,7 @@ class _ContactDetailScreenState extends State<ContactDetailScreen> {
bottom: 20,
right: 20,
child: FloatingActionButton.extended(
backgroundColor: Colors.red,
backgroundColor: contentTheme.primary,
onPressed: () async {
final result = await Get.bottomSheet(
AddCommentBottomSheet(contactId: contactId),

View File

@ -17,13 +17,15 @@ import 'package:marco/view/directory/contact_detail_screen.dart';
import 'package:marco/view/directory/manage_bucket_screen.dart';
import 'package:marco/controller/permission_controller.dart';
import 'package:marco/helpers/utils/permission_constants.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
class DirectoryView extends StatefulWidget {
@override
State<DirectoryView> createState() => _DirectoryViewState();
}
class _DirectoryViewState extends State<DirectoryView> {
class _DirectoryViewState extends State<DirectoryView> with UIMixin {
final DirectoryController controller = Get.find();
final TextEditingController searchController = TextEditingController();
final PermissionController permissionController =
@ -126,7 +128,7 @@ class _DirectoryViewState extends State<DirectoryView> {
child: ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.indigo,
backgroundColor: contentTheme.primary,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
@ -172,7 +174,7 @@ class _DirectoryViewState extends State<DirectoryView> {
backgroundColor: Colors.grey[100],
floatingActionButton: FloatingActionButton.extended(
heroTag: 'createContact',
backgroundColor: Colors.red,
backgroundColor: contentTheme.primary,
onPressed: _handleCreateContact,
icon: const Icon(Icons.person_add_alt_1, color: Colors.white),
label: const Text("Add Contact", style: TextStyle(color: Colors.white)),
@ -246,7 +248,7 @@ class _DirectoryViewState extends State<DirectoryView> {
icon: Icon(Icons.tune,
size: 20,
color: isFilterActive
? Colors.indigo
? contentTheme.primary
: Colors.black87),
onPressed: () {
showModalBottomSheet(
@ -323,13 +325,13 @@ class _DirectoryViewState extends State<DirectoryView> {
PopupMenuItem<int>(
value: 2,
child: Row(
children: const [
children: [
Icon(Icons.add_box_outlined,
size: 20, color: Colors.black87),
SizedBox(width: 10),
Expanded(child: Text("Create Bucket")),
Icon(Icons.chevron_right,
size: 20, color: Colors.red),
size: 20, color: contentTheme.primary),
],
),
onTap: () {
@ -356,13 +358,13 @@ class _DirectoryViewState extends State<DirectoryView> {
PopupMenuItem<int>(
value: 1,
child: Row(
children: const [
children: [
Icon(Icons.label_outline,
size: 20, color: Colors.black87),
SizedBox(width: 10),
Expanded(child: Text("Manage Buckets")),
Icon(Icons.chevron_right,
size: 20, color: Colors.red),
size: 20, color: contentTheme.primary),
],
),
onTap: () {
@ -401,7 +403,7 @@ class _DirectoryViewState extends State<DirectoryView> {
const Expanded(child: Text('Show Deleted Contacts')),
Switch.adaptive(
value: !controller.isActive.value,
activeColor: Colors.indigo,
activeColor: contentTheme.primary ,
onChanged: (val) {
controller.isActive.value = !val;
controller.fetchContacts(active: !val);
@ -424,7 +426,7 @@ class _DirectoryViewState extends State<DirectoryView> {
child: Obx(() {
return MyRefreshIndicator(
onRefresh: _refreshDirectory,
backgroundColor: Colors.indigo,
backgroundColor: contentTheme.primary,
color: Colors.white,
child: controller.isLoading.value
? ListView.separated(

View File

@ -12,6 +12,8 @@ import 'package:marco/helpers/widgets/avatar.dart';
import 'package:marco/helpers/utils/date_time_utils.dart';
import 'package:marco/helpers/widgets/Directory/comment_editor_card.dart';
import 'package:marco/helpers/widgets/my_confirmation_dialog.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
class NotesView extends StatelessWidget {
final NotesController controller = Get.find();

View File

@ -13,6 +13,7 @@ import 'package:marco/helpers/widgets/my_snackbar.dart';
import 'package:marco/model/document/document_edit_bottom_sheet.dart';
import 'package:marco/controller/permission_controller.dart';
import 'package:marco/helpers/utils/permission_constants.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
class DocumentDetailsPage extends StatefulWidget {
final String documentId;
@ -23,7 +24,7 @@ class DocumentDetailsPage extends StatefulWidget {
State<DocumentDetailsPage> createState() => _DocumentDetailsPageState();
}
class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
class _DocumentDetailsPageState extends State<DocumentDetailsPage> with UIMixin {
final DocumentDetailsController controller =
Get.find<DocumentDetailsController>();
@ -155,7 +156,7 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
if (permissionController
.hasPermission(Permissions.modifyDocument))
IconButton(
icon: const Icon(Icons.edit, color: Colors.red),
icon: Icon(Icons.edit, color: contentTheme.primary),
onPressed: () async {
// existing bottom sheet flow
await controller

View File

@ -19,6 +19,7 @@ import 'package:marco/helpers/widgets/my_snackbar.dart';
import 'package:marco/controller/permission_controller.dart';
import 'package:marco/controller/document/document_details_controller.dart';
import 'dart:convert';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
class UserDocumentsPage extends StatefulWidget {
final String? entityId;
@ -34,7 +35,7 @@ class UserDocumentsPage extends StatefulWidget {
State<UserDocumentsPage> createState() => _UserDocumentsPageState();
}
class _UserDocumentsPageState extends State<UserDocumentsPage> {
class _UserDocumentsPageState extends State<UserDocumentsPage> with UIMixin {
final DocumentController docController = Get.put(DocumentController());
final PermissionController permissionController =
Get.find<PermissionController>();
@ -304,6 +305,11 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
);
},
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
borderSide:
BorderSide(color: contentTheme.primary, width: 1.5),
),
hintText: 'Search documents...',
filled: true,
fillColor: Colors.white,
@ -415,7 +421,7 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
const Expanded(child: Text('Show Deleted Documents')),
Switch.adaptive(
value: docController.showInactive.value,
activeColor: Colors.indigo,
activeColor: contentTheme.primary,
onChanged: (val) {
docController.showInactive.value = val;
docController.fetchDocuments(
@ -640,7 +646,7 @@ class _UserDocumentsPageState extends State<UserDocumentsPage> {
color: Colors.white,
fontWeight: 600,
),
backgroundColor: Colors.red,
backgroundColor: contentTheme.primary,
)
: null,
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,

View File

@ -9,6 +9,8 @@ import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/helpers/utils/launcher_utils.dart';
import 'package:marco/helpers/widgets/my_refresh_indicator.dart';
import 'package:marco/model/employees/add_employee_bottom_sheet.dart';
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
class EmployeeDetailPage extends StatefulWidget {
final String employeeId;
@ -24,7 +26,7 @@ class EmployeeDetailPage extends StatefulWidget {
State<EmployeeDetailPage> createState() => _EmployeeDetailPageState();
}
class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
final EmployeesScreenController controller =
Get.put(EmployeesScreenController());
@ -87,7 +89,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
value,
style: TextStyle(
fontWeight: FontWeight.normal,
color: (isEmail || isPhone) ? Colors.indigo : Colors.black54,
color: (isEmail || isPhone) ? contentTheme.primary : Colors.black54,
fontSize: 14,
decoration: (isEmail || isPhone)
? TextDecoration.underline
@ -248,7 +250,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> {
),
IconButton(
icon:
const Icon(Icons.edit, size: 24, color: Colors.red),
Icon(Icons.edit, size: 24, color: contentTheme.primary),
onPressed: () async {
final result =
await showModalBottomSheet<Map<String, dynamic>>(

View File

@ -16,6 +16,7 @@ import 'package:marco/helpers/utils/permission_constants.dart';
import 'package:marco/helpers/widgets/my_refresh_indicator.dart';
import 'package:marco/view/employees/employee_profile_screen.dart';
class EmployeesScreen extends StatefulWidget {
const EmployeesScreen({super.key});
@ -209,14 +210,14 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.red,
color: contentTheme.primary,
borderRadius: BorderRadius.circular(28),
boxShadow: const [
BoxShadow(
color: Colors.black26, blurRadius: 6, offset: Offset(0, 3))
],
),
child: const Row(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.add, color: Colors.white),
@ -262,6 +263,11 @@ class _EmployeesScreenState extends State<EmployeesScreen> with UIMixin {
hintStyle: const TextStyle(fontSize: 13, color: Colors.grey),
filled: true,
fillColor: Colors.white,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
borderSide:
BorderSide(color: contentTheme.primary, width: 1.5),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Colors.grey.shade300, width: 1),

View File

@ -12,7 +12,6 @@ import 'package:marco/helpers/widgets/expense/expense_main_components.dart';
import 'package:marco/helpers/utils/permission_constants.dart';
import 'package:marco/helpers/widgets/my_refresh_indicator.dart';
class ExpenseMainScreen extends StatefulWidget {
const ExpenseMainScreen({super.key});
@ -111,7 +110,8 @@ Widget build(BuildContext context) {
children: [
// ---------------- Search ----------------
Padding(
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 0),
padding:
const EdgeInsets.symmetric(horizontal: 0, vertical: 0),
child: SearchAndFilter(
controller: searchController,
onChanged: (_) => setState(() {}),
@ -136,19 +136,21 @@ Widget build(BuildContext context) {
),
],
),
floatingActionButton:
permissionController.hasPermission(Permissions.expenseUpload)
? FloatingActionButton(
? FloatingActionButton.extended(
backgroundColor: Colors.red,
onPressed: showAddExpenseBottomSheet,
child: const Icon(Icons.add, color: Colors.white),
icon: const Icon(Icons.add, color: Colors.white),
label: const Text(
"Create New Expense",
style: TextStyle(color: Colors.white),
),
)
: null,
);
}
Widget _buildExpenseList({required bool isHistory}) {
return Obx(() {
if (expenseController.isLoading.value &&
@ -197,4 +199,3 @@ Widget build(BuildContext context) {
});
}
}

View File

@ -194,8 +194,8 @@ class _UserProfileBarState extends State<UserProfileBar>
_menuItemRow(
icon: LucideIcons.lock,
label: hasMpin ? 'Change MPIN' : 'Set MPIN',
iconColor: Colors.redAccent,
textColor: Colors.redAccent,
iconColor: contentTheme.primary,
textColor: contentTheme.primary,
onTap: _onMpinTap,
),
],
@ -281,8 +281,8 @@ class _UserProfileBarState extends State<UserProfileBar>
foregroundColor: Colors.white,
shadowColor: Colors.red.shade200,
padding: EdgeInsets.symmetric(
vertical: condensed ? 14 : 18,
horizontal: condensed ? 14 : 22,
vertical: condensed ? 9 : 12,
horizontal: condensed ? 6 : 16,
),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),