feat: Add recommended tasks for next sprint to improve coding practices in mobile applications

This commit is contained in:
Achintha Isuru
2026-02-01 14:03:43 -05:00
parent 9f8b60d571
commit 00999503e1
5 changed files with 324 additions and 55 deletions

View File

@@ -0,0 +1,9 @@
## Recommended tasks for the next sprint
* In the mobile applications, since the structure is now finalized (at least for the existing features), we need to **strictly follow best practices while coding**:
* Break down large widgets into **smaller, reusable widgets**
* Add **doc comments** where necessary to improve readability and maintainability
* **Remove overly complicated or unnecessary logic** introduced by AI and simplify where possible
* **Adhere to the design system** and remove all **hard-coded colors and typography**, using shared tokens instead

View File

@@ -12,6 +12,7 @@ import 'package:client_hubs/client_hubs.dart' as client_hubs;
import 'package:client_create_order/client_create_order.dart'
as client_create_order;
import 'package:firebase_core/firebase_core.dart';
import 'package:krow_core/core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -54,34 +55,38 @@ class AppWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider<core_localization.LocaleBloc>(
create: (BuildContext context) =>
Modular.get<core_localization.LocaleBloc>(),
child:
BlocBuilder<
core_localization.LocaleBloc,
core_localization.LocaleState
>(
builder:
(BuildContext context, core_localization.LocaleState state) {
return core_localization.TranslationProvider(
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
title: "Krow Client",
theme: UiTheme.light,
routerConfig: Modular.routerConfig,
locale: state.locale,
supportedLocales: state.supportedLocales,
localizationsDelegates:
const <LocalizationsDelegate<dynamic>>[
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
),
);
},
),
return WebMobileFrame(
appName: 'KROW Client\nApplication',
logo: Image.asset('assets/logo.png'),
child: BlocProvider<core_localization.LocaleBloc>(
create: (BuildContext context) =>
Modular.get<core_localization.LocaleBloc>(),
child:
BlocBuilder<
core_localization.LocaleBloc,
core_localization.LocaleState
>(
builder:
(BuildContext context, core_localization.LocaleState state) {
return core_localization.TranslationProvider(
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
title: "Krow Client",
theme: UiTheme.light,
routerConfig: Modular.routerConfig,
locale: state.locale,
supportedLocales: state.supportedLocales,
localizationsDelegates:
const <LocalizationsDelegate<dynamic>>[
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
),
);
},
),
),
);
}
}

View File

@@ -8,6 +8,7 @@ import 'package:flutter_modular/flutter_modular.dart';
import 'package:staff_authentication/staff_authentication.dart'
as staff_authentication;
import 'package:staff_main/staff_main.dart' as staff_main;
import 'package:krow_core/core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -34,33 +35,37 @@ class AppWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider<core_localization.LocaleBloc>(
create: (BuildContext context) =>
Modular.get<core_localization.LocaleBloc>(),
child:
BlocBuilder<
core_localization.LocaleBloc,
core_localization.LocaleState
>(
builder:
(BuildContext context, core_localization.LocaleState state) {
return core_localization.TranslationProvider(
child: MaterialApp.router(
title: "KROW Staff",
theme: UiTheme.light,
routerConfig: Modular.routerConfig,
locale: state.locale,
supportedLocales: state.supportedLocales,
localizationsDelegates:
const <LocalizationsDelegate<dynamic>>[
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
),
);
},
),
return WebMobileFrame(
appName: 'KROW Staff\nApplication',
logo: Image.asset('assets/logo.png'),
child: BlocProvider<core_localization.LocaleBloc>(
create: (BuildContext context) =>
Modular.get<core_localization.LocaleBloc>(),
child:
BlocBuilder<
core_localization.LocaleBloc,
core_localization.LocaleState
>(
builder:
(BuildContext context, core_localization.LocaleState state) {
return core_localization.TranslationProvider(
child: MaterialApp.router(
title: "KROW Staff",
theme: UiTheme.light,
routerConfig: Modular.routerConfig,
locale: state.locale,
supportedLocales: state.supportedLocales,
localizationsDelegates:
const <LocalizationsDelegate<dynamic>>[
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
),
);
},
),
),
);
}
}

View File

@@ -3,3 +3,4 @@ library core;
export 'src/domain/arguments/usecase_argument.dart';
export 'src/domain/usecases/usecase.dart';
export 'src/utils/date_time_utils.dart';
export 'src/presentation/widgets/web_mobile_frame.dart';

View File

@@ -0,0 +1,249 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
/// A wrapper widget that renders the application inside an iPhone-like frame
/// specifically for Flutter Web. On other platforms, it simply returns the child.
class WebMobileFrame extends StatelessWidget {
final Widget child;
final Widget logo;
final String appName;
const WebMobileFrame({
super.key,
required this.child,
required this.logo,
required this.appName,
});
@override
Widget build(BuildContext context) {
if (!kIsWeb) return child;
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
home: _WebFrameContent(
logo: logo,
appName: appName,
child: child,
),
);
}
}
class _WebFrameContent extends StatefulWidget {
final Widget child;
final Widget logo;
final String appName;
const _WebFrameContent({
required this.child,
required this.logo,
required this.appName,
});
@override
State<_WebFrameContent> createState() => _WebFrameContentState();
}
class _WebFrameContentState extends State<_WebFrameContent> {
// ignore: unused_field
Offset _cursorPosition = Offset.zero;
// ignore: unused_field
bool _isHovering = false;
@override
Widget build(BuildContext context) {
// iPhone 14 Pro Max-ish dimensions (scaled for frame look)
const double frameWidth = 390 * 1.2;
const double frameHeight = 844 * 1.3;
const double borderRadius = 54.0;
const double borderThickness = 12.0;
return Scaffold(
backgroundColor: const Color(0xFF121212),
body: MouseRegion(
cursor: SystemMouseCursors.none,
onHover: (event) {
setState(() {
_cursorPosition = event.position;
_isHovering = true;
});
},
onExit: (_) => setState(() => _isHovering = false),
child: Stack(
children: [
// Logo and Title on the left (Web only)
Positioned(
left: 60,
top: 0,
bottom: 0,
child: Center(
child: Opacity(
opacity: 0.5,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 140,
child: widget.logo,
),
const SizedBox(height: 12),
Text(
widget.appName,
textAlign: TextAlign.left,
style: const TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold,
letterSpacing: -0.5,
fontFamily: 'Instrument Sans', // Fallback if available or system
),
),
const SizedBox(height: 4),
Container(
height: 2,
width: 40,
color: Colors.white.withOpacity(0.3),
),
],
),
),
),
),
// Frame and Content
Center(
child: LayoutBuilder(
builder: (context, constraints) {
// Scale down if screen is too small
double scaleX = constraints.maxWidth / (frameWidth + 80);
double scaleY = constraints.maxHeight / (frameHeight + 80);
double scale = (scaleX < 1 || scaleY < 1)
? (scaleX < scaleY ? scaleX : scaleY)
: 1.0;
return Transform.scale(
scale: scale,
child: Container(
width: frameWidth,
height: frameHeight,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(borderRadius),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.6),
blurRadius: 40,
spreadRadius: 10,
),
],
border: Border.all(
color: const Color(0xFF2C2C2C),
width: borderThickness,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(
borderRadius - borderThickness,
),
child: Stack(
children: [
// The actual app + status bar
Column(
children: [
// Mock iOS Status Bar
Container(
height: 48,
padding: const EdgeInsets.symmetric(
horizontal: 24,
),
decoration: const BoxDecoration(
color: Color(0xFFF9F6EE),
border: Border(
bottom: BorderSide(
color: Color(0xFFEEEEEE),
width: 0.5,
),
),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
// Time side
const SizedBox(
width: 80,
child: Text(
'9:41 PM',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black54,
fontWeight: FontWeight.w700,
fontSize: 14,
letterSpacing: -0.2,
),
),
),
// Status Icons side
const SizedBox(
width: 80,
child: Row(
mainAxisAlignment:
MainAxisAlignment.end,
spacing: 12,
children: [
Icon(
Icons.signal_cellular_alt,
size: 14,
color: Colors.black54,
),
Icon(
Icons.wifi,
size: 14,
color: Colors.black54,
),
Icon(
Icons.battery_full,
size: 14,
color: Colors.black54,
),
],
),
),
],
),
),
// The main app content content
Expanded(child: widget.child),
],
),
// Dynamic Island / Notch Mockup
Align(
alignment: Alignment.topCenter,
child: Container(
width: 120,
height: 35,
margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(20),
),
),
),
],
),
),
),
);
},
),
),
],
),
),
);
}
}