Compare commits
No commits in common. "login-screen" and "main" have entirely different histories.
login-scre
...
main
@ -1,6 +1,4 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
|
||||||
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
|
|
||||||
<application
|
<application
|
||||||
android:label="kmobile"
|
android:label="kmobile"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 26 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.7 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 26 KiB |
@ -45,7 +45,5 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSFaceIDUsageDescription</key>
|
|
||||||
<string>We use Face ID to secure your data.</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -1,20 +1,14 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import '../../app.dart';
|
|
||||||
import '../../features/auth/models/auth_token.dart';
|
import '../../features/auth/models/auth_token.dart';
|
||||||
import '../../features/auth/models/auth_credentials.dart';
|
import '../../features/auth/models/auth_credentials.dart';
|
||||||
import '../../data/models/user.dart';
|
import '../../data/models/user.dart';
|
||||||
import '../../core/errors/exceptions.dart';
|
import '../../core/errors/exceptions.dart';
|
||||||
import 'package:local_auth/local_auth.dart';
|
|
||||||
|
|
||||||
import '../../features/dashboard/screens/dashboard_screen.dart';
|
|
||||||
|
|
||||||
class AuthService {
|
class AuthService {
|
||||||
final Dio _dio;
|
final Dio _dio;
|
||||||
|
|
||||||
AuthService(this._dio);
|
AuthService(this._dio);
|
||||||
|
|
||||||
Future<AuthToken> login(AuthCredentials credentials) async {
|
Future<AuthToken> login(AuthCredentials credentials) async {
|
||||||
|
103
lib/app.dart
103
lib/app.dart
@ -1,20 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:kmobile/features/auth/screens/customer_info_screen.dart';
|
|
||||||
import 'package:kmobile/security/secure_storage.dart';
|
|
||||||
import 'api/services/auth_service.dart';
|
|
||||||
import 'config/themes.dart';
|
import 'config/themes.dart';
|
||||||
import 'config/routes.dart';
|
import 'config/routes.dart';
|
||||||
import 'data/repositories/auth_repository.dart';
|
|
||||||
import 'di/injection.dart';
|
import 'di/injection.dart';
|
||||||
import 'features/auth/controllers/auth_cubit.dart';
|
import 'features/auth/controllers/auth_cubit.dart';
|
||||||
import 'features/auth/controllers/auth_state.dart';
|
import 'features/auth/controllers/auth_state.dart';
|
||||||
import 'features/auth/screens/Card_screen.dart';
|
|
||||||
import 'features/auth/screens/login_screen.dart';
|
import 'features/auth/screens/login_screen.dart';
|
||||||
import 'features/auth/screens/service_screen.dart';
|
|
||||||
import 'features/dashboard/screens/dashboard_screen.dart';
|
import 'features/dashboard/screens/dashboard_screen.dart';
|
||||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
|
||||||
|
|
||||||
class KMobile extends StatelessWidget {
|
class KMobile extends StatelessWidget {
|
||||||
const KMobile({super.key});
|
const KMobile({super.key});
|
||||||
@ -59,12 +52,8 @@ class KMobile extends StatelessWidget {
|
|||||||
return const _SplashScreen();
|
return const _SplashScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(state is ShowBiometricPermission){
|
|
||||||
return const BiometricPermissionScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state is Authenticated) {
|
if (state is Authenticated) {
|
||||||
return const NavigationScaffold();
|
return const DashboardScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
return const LoginScreen();
|
return const LoginScreen();
|
||||||
@ -115,93 +104,3 @@ class _SplashScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NavigationScaffold extends StatefulWidget {
|
|
||||||
const NavigationScaffold({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<NavigationScaffold> createState() => _NavigationScaffoldState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NavigationScaffoldState extends State<NavigationScaffold> {
|
|
||||||
final PageController _pageController = PageController();
|
|
||||||
int _selectedIndex = 0;
|
|
||||||
|
|
||||||
final List<Widget> _pages = [
|
|
||||||
DashboardScreen(),
|
|
||||||
CardScreen(),
|
|
||||||
ServiceScreen(),
|
|
||||||
CustomerInfoScreen()
|
|
||||||
];
|
|
||||||
|
|
||||||
void _onItemTapped(int index) {
|
|
||||||
setState(() {
|
|
||||||
_selectedIndex = index;
|
|
||||||
});
|
|
||||||
_pageController.jumpToPage(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
body: PageView(
|
|
||||||
controller: _pageController,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
children: _pages,
|
|
||||||
),
|
|
||||||
bottomNavigationBar: BottomNavigationBar(
|
|
||||||
currentIndex: _selectedIndex,
|
|
||||||
type: BottomNavigationBarType.fixed,
|
|
||||||
items: const [
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.home_filled),
|
|
||||||
label: 'Home',
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Symbols.credit_card),
|
|
||||||
label: 'Card',
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
|
||||||
icon: Icon(Symbols.concierge),
|
|
||||||
label: 'Services',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onTap: _onItemTapped,
|
|
||||||
backgroundColor: const Color(0xFFE0F7FA), // Light blue background
|
|
||||||
selectedItemColor: Colors.blue[800],
|
|
||||||
unselectedItemColor: Colors.black54,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BiometricPermissionScreen extends StatelessWidget {
|
|
||||||
const BiometricPermissionScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final cubit = context.read<AuthCubit>();
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Text('Would you like to enable biometric authentication?'),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => cubit.handleBiometricChoice(true),
|
|
||||||
child: const Text('Yes'),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => cubit.handleBiometricChoice(false),
|
|
||||||
child: const Text('No, thanks'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:kmobile/features/auth/screens/customer_info_screen.dart';
|
|
||||||
import 'package:kmobile/features/auth/screens/mpin_screen.dart';
|
|
||||||
import '../app.dart';
|
|
||||||
import '../features/auth/screens/login_screen.dart';
|
import '../features/auth/screens/login_screen.dart';
|
||||||
// import '../features/auth/screens/forgot_password_screen.dart';
|
// import '../features/auth/screens/forgot_password_screen.dart';
|
||||||
// import '../features/auth/screens/register_screen.dart';
|
// import '../features/auth/screens/register_screen.dart';
|
||||||
@ -18,15 +15,12 @@ class AppRoutes {
|
|||||||
// Route names
|
// Route names
|
||||||
static const String splash = '/';
|
static const String splash = '/';
|
||||||
static const String login = '/login';
|
static const String login = '/login';
|
||||||
static const String mPin = '/mPin';
|
|
||||||
static const String register = '/register';
|
static const String register = '/register';
|
||||||
static const String forgotPassword = '/forgot-password';
|
static const String forgotPassword = '/forgot-password';
|
||||||
static const String navigationScaffold = '/navigation-scaffold';
|
|
||||||
static const String dashboard = '/dashboard';
|
static const String dashboard = '/dashboard';
|
||||||
static const String accounts = '/accounts';
|
static const String accounts = '/accounts';
|
||||||
static const String transactions = '/transactions';
|
static const String transactions = '/transactions';
|
||||||
static const String payments = '/payments';
|
static const String payments = '/payments';
|
||||||
static const String customer_info = '/customer-info';
|
|
||||||
|
|
||||||
// Route generator
|
// Route generator
|
||||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
static Route<dynamic> generateRoute(RouteSettings settings) {
|
||||||
@ -34,9 +28,6 @@ class AppRoutes {
|
|||||||
case login:
|
case login:
|
||||||
return MaterialPageRoute(builder: (_) => const LoginScreen());
|
return MaterialPageRoute(builder: (_) => const LoginScreen());
|
||||||
|
|
||||||
case mPin:
|
|
||||||
return MaterialPageRoute(builder: (_) => const MPinScreen());
|
|
||||||
|
|
||||||
case register:
|
case register:
|
||||||
// Placeholder - create the RegisterScreen class and uncomment
|
// Placeholder - create the RegisterScreen class and uncomment
|
||||||
// return MaterialPageRoute(builder: (_) => const RegisterScreen());
|
// return MaterialPageRoute(builder: (_) => const RegisterScreen());
|
||||||
@ -47,15 +38,9 @@ class AppRoutes {
|
|||||||
// return MaterialPageRoute(builder: (_) => const ForgotPasswordScreen());
|
// return MaterialPageRoute(builder: (_) => const ForgotPasswordScreen());
|
||||||
return _errorRoute();
|
return _errorRoute();
|
||||||
|
|
||||||
case navigationScaffold:
|
|
||||||
return MaterialPageRoute(builder: (_) => const NavigationScaffold());
|
|
||||||
|
|
||||||
case dashboard:
|
case dashboard:
|
||||||
return MaterialPageRoute(builder: (_) => const DashboardScreen());
|
return MaterialPageRoute(builder: (_) => const DashboardScreen());
|
||||||
|
|
||||||
case customer_info:
|
|
||||||
return MaterialPageRoute(builder: (_) => const CustomerInfoScreen());
|
|
||||||
|
|
||||||
case accounts:
|
case accounts:
|
||||||
// Placeholder - create the AccountsScreen class and uncomment
|
// Placeholder - create the AccountsScreen class and uncomment
|
||||||
// return MaterialPageRoute(builder: (_) => const AccountsScreen());
|
// return MaterialPageRoute(builder: (_) => const AccountsScreen());
|
||||||
|
@ -22,61 +22,51 @@ class AppThemes {
|
|||||||
fontSize: 96,
|
fontSize: 96,
|
||||||
fontWeight: FontWeight.w300,
|
fontWeight: FontWeight.w300,
|
||||||
color: Color(0xFF212121),
|
color: Color(0xFF212121),
|
||||||
fontFamily: 'Rubik',
|
|
||||||
),
|
),
|
||||||
displayMedium: TextStyle(
|
displayMedium: TextStyle(
|
||||||
fontSize: 60,
|
fontSize: 60,
|
||||||
fontWeight: FontWeight.w300,
|
fontWeight: FontWeight.w300,
|
||||||
color: Color(0xFF212121),
|
color: Color(0xFF212121),
|
||||||
fontFamily: 'Rubik',
|
|
||||||
),
|
),
|
||||||
displaySmall: TextStyle(
|
displaySmall: TextStyle(
|
||||||
fontSize: 48,
|
fontSize: 48,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Color(0xFF212121),
|
color: Color(0xFF212121),
|
||||||
fontFamily: 'Rubik',
|
|
||||||
),
|
),
|
||||||
headlineMedium: TextStyle(
|
headlineMedium: TextStyle(
|
||||||
fontSize: 34,
|
fontSize: 34,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Color(0xFF212121),
|
color: Color(0xFF212121),
|
||||||
fontFamily: 'Rubik',
|
|
||||||
),
|
),
|
||||||
headlineSmall: TextStyle(
|
headlineSmall: TextStyle(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Color(0xFF212121),
|
color: Color(0xFF212121),
|
||||||
fontFamily: 'Rubik',
|
|
||||||
),
|
),
|
||||||
titleLarge: TextStyle(
|
titleLarge: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Color(0xFF212121),
|
color: Color(0xFF212121),
|
||||||
fontFamily: 'Rubik',
|
|
||||||
),
|
),
|
||||||
bodyLarge: TextStyle(
|
bodyLarge: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Color(0xFF212121),
|
color: Color(0xFF212121),
|
||||||
fontFamily: 'Rubik',
|
|
||||||
),
|
),
|
||||||
bodyMedium: TextStyle(
|
bodyMedium: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Color(0xFF212121),
|
color: Color(0xFF212121),
|
||||||
fontFamily: 'Rubik',
|
|
||||||
),
|
),
|
||||||
bodySmall: TextStyle(
|
bodySmall: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Color(0xFF757575),
|
color: Color(0xFF757575),
|
||||||
fontFamily: 'Rubik',
|
|
||||||
),
|
),
|
||||||
labelLarge: TextStyle(
|
labelLarge: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Color(0xFF212121),
|
color: Color(0xFF212121),
|
||||||
fontFamily: 'Rubik',
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -96,7 +86,6 @@ class AppThemes {
|
|||||||
// Light theme
|
// Light theme
|
||||||
static final ThemeData lightTheme = ThemeData(
|
static final ThemeData lightTheme = ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
fontFamily: 'Rubik',
|
|
||||||
colorScheme: const ColorScheme.light(
|
colorScheme: const ColorScheme.light(
|
||||||
primary: _primaryColorLight,
|
primary: _primaryColorLight,
|
||||||
secondary: _secondaryColorLight,
|
secondary: _secondaryColorLight,
|
||||||
@ -178,7 +167,6 @@ class AppThemes {
|
|||||||
|
|
||||||
// Dark theme
|
// Dark theme
|
||||||
static final ThemeData darkTheme = ThemeData(
|
static final ThemeData darkTheme = ThemeData(
|
||||||
fontFamily: 'Rubik',
|
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
colorScheme: const ColorScheme.dark(
|
colorScheme: const ColorScheme.dark(
|
||||||
primary: _primaryColorDark,
|
primary: _primaryColorDark,
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:local_auth/local_auth.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import '../../../app.dart';
|
|
||||||
import '../../../data/repositories/auth_repository.dart';
|
import '../../../data/repositories/auth_repository.dart';
|
||||||
import 'auth_state.dart';
|
import 'auth_state.dart';
|
||||||
|
|
||||||
@ -53,60 +47,4 @@ class AuthCubit extends Cubit<AuthState> {
|
|||||||
emit(AuthError(e.toString()));
|
emit(AuthError(e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> checkFirstLaunch() async {
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
final isFirstLaunch = prefs.getBool('isFirstLaunch') ?? true;
|
|
||||||
|
|
||||||
if (isFirstLaunch) {
|
|
||||||
emit(ShowBiometricPermission());
|
|
||||||
} else {
|
|
||||||
// Continue to authentication logic (e.g., check token)
|
|
||||||
emit(AuthLoading()); // or Unauthenticated/Authenticated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> handleBiometricChoice(bool enabled) async {
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
await prefs.setBool('biometric_opt_in', enabled);
|
|
||||||
await prefs.setBool('isFirstLaunch', false);
|
|
||||||
|
|
||||||
// Then continue to auth logic or home
|
|
||||||
if (enabled) {
|
|
||||||
authenticateBiometric(); // implement biometric logic
|
|
||||||
} else {
|
|
||||||
emit(Unauthenticated());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> authenticateBiometric() async {
|
|
||||||
final LocalAuthentication auth = LocalAuthentication();
|
|
||||||
|
|
||||||
try {
|
|
||||||
final isAvailable = await auth.canCheckBiometrics;
|
|
||||||
final isDeviceSupported = await auth.isDeviceSupported();
|
|
||||||
|
|
||||||
if (isAvailable && isDeviceSupported) {
|
|
||||||
final authenticated = await auth.authenticate(
|
|
||||||
localizedReason: 'Touch the fingerprint sensor',
|
|
||||||
options: const AuthenticationOptions(
|
|
||||||
biometricOnly: true,
|
|
||||||
stickyAuth: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (authenticated) {
|
|
||||||
// Continue to normal auth logic (e.g., auto login)
|
|
||||||
emit(AuthLoading());
|
|
||||||
await checkAuthStatus(); // Your existing method to verify token/session
|
|
||||||
} else {
|
|
||||||
emit(Unauthenticated());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
emit(Unauthenticated());
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
emit(Unauthenticated());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,6 @@ class AuthInitial extends AuthState {}
|
|||||||
|
|
||||||
class AuthLoading extends AuthState {}
|
class AuthLoading extends AuthState {}
|
||||||
|
|
||||||
class ShowBiometricPermission extends AuthState {}
|
|
||||||
|
|
||||||
class Authenticated extends AuthState {
|
class Authenticated extends AuthState {
|
||||||
final User user;
|
final User user;
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class CardScreen extends StatefulWidget {
|
|
||||||
const CardScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<CardScreen> createState() => _CardScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CardScreen extends State<CardScreen>{
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
|
||||||
|
|
||||||
class CustomerInfoScreen extends StatefulWidget {
|
|
||||||
const CustomerInfoScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<CustomerInfoScreen> createState() => _CustomerInfoScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CustomerInfoScreen extends State<CustomerInfoScreen>{
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
leading: IconButton(icon: const Icon(Symbols.arrow_back_ios_new),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},),
|
|
||||||
title: const Text('kMobile', style: TextStyle(color: Colors.black,
|
|
||||||
fontWeight: FontWeight.w500),),
|
|
||||||
actions: const [
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(right: 10.0),
|
|
||||||
child: CircleAvatar(
|
|
||||||
backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image
|
|
||||||
radius: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
body: const SingleChildScrollView(
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(16.0),
|
|
||||||
child: SafeArea(
|
|
||||||
child: Center(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(height: 30),
|
|
||||||
CircleAvatar(
|
|
||||||
backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image
|
|
||||||
radius: 50,
|
|
||||||
),
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(top: 10.0),
|
|
||||||
child: Text('Trina Bakshi', style: TextStyle(fontSize: 20,
|
|
||||||
color: Colors.black, fontWeight: FontWeight.w500),),
|
|
||||||
),
|
|
||||||
|
|
||||||
Text('CIF: 2553677487774', style: TextStyle(fontSize: 16, color: Colors.grey),),
|
|
||||||
SizedBox(height: 30,),
|
|
||||||
InfoField(label: 'Number of Active Accounts', value: '3'),
|
|
||||||
InfoField(label: 'Mobile Number', value: '987XXXXX78'),
|
|
||||||
InfoField(label: 'Date of Birth', value: '12-07-1984'),
|
|
||||||
InfoField(label: 'Branch', value: 'Krishnapur'),
|
|
||||||
InfoField(label: 'Aadhar Number', value: '7665 XXXX 1276'),
|
|
||||||
InfoField(label: 'PAN Number', value: '700127638009871'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InfoField extends StatelessWidget {
|
|
||||||
final String label;
|
|
||||||
final String value;
|
|
||||||
|
|
||||||
const InfoField({Key? key, required this.label, required this.value}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 3),
|
|
||||||
Text(
|
|
||||||
value,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
|
||||||
import 'package:kmobile/features/auth/screens/mpin_screen.dart';
|
|
||||||
import '../../../app.dart';
|
|
||||||
import '../controllers/auth_cubit.dart';
|
import '../controllers/auth_cubit.dart';
|
||||||
import '../controllers/auth_state.dart';
|
import '../controllers/auth_state.dart';
|
||||||
import '../../dashboard/screens/dashboard_screen.dart';
|
import '../../dashboard/screens/dashboard_screen.dart';
|
||||||
@ -16,39 +13,35 @@ class LoginScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class LoginScreenState extends State<LoginScreen> {
|
class LoginScreenState extends State<LoginScreen> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final _customerNumberController = TextEditingController();
|
final _usernameController = TextEditingController();
|
||||||
final _passwordController = TextEditingController();
|
final _passwordController = TextEditingController();
|
||||||
bool _obscurePassword = true;
|
bool _obscurePassword = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_customerNumberController.dispose();
|
_usernameController.dispose();
|
||||||
_passwordController.dispose();
|
_passwordController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _submitForm() {
|
void _submitForm() {
|
||||||
// if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
// context.read<AuthCubit>().login(
|
context.read<AuthCubit>().login(
|
||||||
// _customerNumberController.text.trim(),
|
_usernameController.text.trim(),
|
||||||
// _passwordController.text,
|
_passwordController.text,
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
Navigator.of(context).pushReplacement(
|
|
||||||
MaterialPageRoute(builder: (context) => const MPinScreen()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
// appBar: AppBar(title: const Text('Login')),
|
appBar: AppBar(title: const Text('Login')),
|
||||||
body: BlocConsumer<AuthCubit, AuthState>(
|
body: BlocConsumer<AuthCubit, AuthState>(
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
if (state is Authenticated) {
|
if (state is Authenticated) {
|
||||||
Navigator.of(context).pushReplacement(
|
Navigator.of(context).pushReplacement(
|
||||||
MaterialPageRoute(builder: (context) => const NavigationScaffold()),
|
MaterialPageRoute(builder: (context) => const DashboardScreen()),
|
||||||
);
|
);
|
||||||
} else if (state is AuthError) {
|
} else if (state is AuthError) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
@ -63,35 +56,19 @@ class LoginScreenState extends State<LoginScreen> {
|
|||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
// Bank logo or app branding
|
// Bank logo or app branding
|
||||||
SvgPicture.asset('assets/images/kccb_logo.svg', width: 100, height: 100,),
|
const FlutterLogo(size: 80),
|
||||||
const SizedBox(height: 16),
|
|
||||||
// Title
|
|
||||||
const Text(
|
|
||||||
'KCCB',
|
|
||||||
style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Colors.blue),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 48),
|
const SizedBox(height: 48),
|
||||||
|
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _customerNumberController,
|
controller: _usernameController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Customer Number',
|
labelText: 'Username',
|
||||||
// prefixIcon: Icon(Icons.person),
|
prefixIcon: Icon(Icons.person),
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
isDense: true,
|
|
||||||
filled: true,
|
|
||||||
fillColor: Colors.white,
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: Colors.black),
|
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: Colors.black, width: 2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
textInputAction: TextInputAction.next,
|
textInputAction: TextInputAction.next,
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
@ -106,17 +83,8 @@ class LoginScreenState extends State<LoginScreen> {
|
|||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Password',
|
labelText: 'Password',
|
||||||
// prefixIcon: const Icon(Icons.lock),
|
prefixIcon: const Icon(Icons.lock),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
isDense: true,
|
|
||||||
filled: true,
|
|
||||||
fillColor: Colors.white,
|
|
||||||
enabledBorder: const OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: Colors.black),
|
|
||||||
),
|
|
||||||
focusedBorder: const OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: Colors.black, width: 2),
|
|
||||||
),
|
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
_obscurePassword
|
_obscurePassword
|
||||||
@ -139,72 +107,43 @@ class LoginScreenState extends State<LoginScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
// Align(
|
Align(
|
||||||
// alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
// child: TextButton(
|
child: TextButton(
|
||||||
// onPressed: () {
|
onPressed: () {
|
||||||
// // Navigate to forgot password screen
|
// Navigate to forgot password screen
|
||||||
// },
|
},
|
||||||
// child: const Text('Forgot Password?'),
|
child: const Text('Forgot Password?'),
|
||||||
// ),
|
),
|
||||||
// ),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
SizedBox(
|
ElevatedButton(
|
||||||
width: 250,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: state is AuthLoading ? null : _submitForm,
|
onPressed: state is AuthLoading ? null : _submitForm,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
shape: const StadiumBorder(),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
backgroundColor: Colors.white,
|
|
||||||
foregroundColor: Colors.blueAccent,
|
|
||||||
side: const BorderSide(color: Colors.black, width: 1),
|
|
||||||
elevation: 0
|
|
||||||
),
|
),
|
||||||
child: state is AuthLoading
|
child: state is AuthLoading
|
||||||
? const CircularProgressIndicator()
|
? const CircularProgressIndicator()
|
||||||
: const Text('Login', style: TextStyle(fontSize: 16),),
|
: const Text('LOGIN', style: TextStyle(fontSize: 16)),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
// OR Divider
|
// Registration option
|
||||||
const Padding(
|
Row(
|
||||||
padding: EdgeInsets.symmetric(vertical: 16),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Divider()),
|
const Text("Don't have an account?"),
|
||||||
Padding(
|
TextButton(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
|
||||||
child: Text('OR'),
|
|
||||||
),
|
|
||||||
Expanded(child: Divider()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 25),
|
|
||||||
|
|
||||||
// Register Button
|
|
||||||
SizedBox(
|
|
||||||
width: 250,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Handle register
|
// Navigate to registration screen
|
||||||
},
|
},
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
shape: const StadiumBorder(),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
||||||
backgroundColor: Colors.lightBlue[100],
|
|
||||||
foregroundColor: Colors.black
|
|
||||||
),
|
|
||||||
child: const Text('Register'),
|
child: const Text('Register'),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:kmobile/features/auth/controllers/auth_cubit.dart';
|
|
||||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
|
||||||
|
|
||||||
import '../../../app.dart';
|
|
||||||
|
|
||||||
class MPinScreen extends StatefulWidget {
|
|
||||||
const MPinScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
MPinScreenState createState() => MPinScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class MPinScreenState extends State<MPinScreen> {
|
|
||||||
List<String> mPin = [];
|
|
||||||
|
|
||||||
void addDigit(String digit) {
|
|
||||||
if (mPin.length < 4) {
|
|
||||||
setState(() {
|
|
||||||
mPin.add(digit);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void deleteDigit() {
|
|
||||||
if (mPin.isNotEmpty) {
|
|
||||||
setState(() {
|
|
||||||
mPin.removeLast();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildMPinDots() {
|
|
||||||
return Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: List.generate(4, (index) {
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.all(8),
|
|
||||||
width: 15,
|
|
||||||
height: 15,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: index < mPin.length ? Colors.black : Colors.grey[400],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildNumberPad() {
|
|
||||||
List<List<String>> keys = [
|
|
||||||
['1', '2', '3'],
|
|
||||||
['4', '5', '6'],
|
|
||||||
['7', '8', '9'],
|
|
||||||
['Enter', '0', '<']
|
|
||||||
];
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
children: keys.map((row) {
|
|
||||||
return Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: row.map((key) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
if (key == '<') {
|
|
||||||
deleteDigit();
|
|
||||||
} else if (key == 'Enter') {
|
|
||||||
if (mPin.length == 4) {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(builder: (context) => const NavigationScaffold()),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
const SnackBar(content: Text("Please enter 4 digits")),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (key.isNotEmpty) {
|
|
||||||
addDigit(key);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
width: 70,
|
|
||||||
height: 70,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: Colors.grey[200],
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: key == 'Enter' ? const Icon(Symbols.check) : Text(
|
|
||||||
key == '<' ? '⌫' : key,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: key == 'Enter' ?
|
|
||||||
FontWeight.normal : FontWeight.normal,
|
|
||||||
color: key == 'Enter' ? Colors.blue : Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final cubit = context.read<AuthCubit>();
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
body: SafeArea(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Spacer(),
|
|
||||||
// Logo
|
|
||||||
const FlutterLogo(size: 100),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
const Text(
|
|
||||||
"Enter your mPIN",
|
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
buildMPinDots(),
|
|
||||||
const Spacer(),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
TextButton(onPressed: () {
|
|
||||||
cubit.authenticateBiometric();
|
|
||||||
}, child: const Text("Try another way")),
|
|
||||||
TextButton(onPressed: () {}, child: const Text("Register?")),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
buildNumberPad(),
|
|
||||||
const Spacer(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class ServiceScreen extends StatefulWidget {
|
|
||||||
const ServiceScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ServiceScreen> createState() => _ServiceScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ServiceScreen extends State<ServiceScreen>{
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:kmobile/features/auth/screens/customer_info_screen.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
import '../../auth/controllers/auth_cubit.dart';
|
||||||
|
import '../../auth/controllers/auth_state.dart';
|
||||||
|
import '../widgets/account_card.dart';
|
||||||
|
import '../widgets/transaction_list_item.dart';
|
||||||
|
import '../../accounts/models/account.dart';
|
||||||
|
import '../../transactions/models/transaction.dart';
|
||||||
|
|
||||||
class DashboardScreen extends StatefulWidget {
|
class DashboardScreen extends StatefulWidget {
|
||||||
const DashboardScreen({super.key});
|
const DashboardScreen({super.key});
|
||||||
@ -10,150 +15,331 @@ class DashboardScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DashboardScreenState extends State<DashboardScreen> {
|
class _DashboardScreenState extends State<DashboardScreen> {
|
||||||
// Mock data for transactions
|
// Mock data for demonstration
|
||||||
final List<Map<String, String>> transactions = [
|
final List<Account> _accounts = [
|
||||||
{'name': 'Raj Kumar', 'amount': '₹1,000', 'date': '09 March, 2025 16:04', 'type': 'in'},
|
Account(
|
||||||
{'name': 'Sunita Joshi', 'amount': '₹1,45,000', 'date': '07 March, 2025 16:04', 'type': 'out'},
|
id: '1',
|
||||||
{'name': 'Manoj Singh', 'amount': '₹2,400', 'date': '07 March, 2025 16:04', 'type': 'in'},
|
accountNumber: '**** 4589',
|
||||||
{'name': 'Raj Kumar', 'amount': '₹11,500', 'date': '09 March, 2025 16:04', 'type': 'in'},
|
accountType: 'Savings',
|
||||||
{'name': 'Manoj Singh', 'amount': '₹1,000', 'date': '', 'type': 'in'},
|
balance: 12450.75,
|
||||||
|
currency: 'USD',
|
||||||
|
),
|
||||||
|
Account(
|
||||||
|
id: '2',
|
||||||
|
accountNumber: '**** 7823',
|
||||||
|
accountType: 'Checking',
|
||||||
|
balance: 3840.50,
|
||||||
|
currency: 'USD',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
final List<Transaction> _recentTransactions = [
|
||||||
|
Transaction(
|
||||||
|
id: 't1',
|
||||||
|
description: 'Coffee Shop',
|
||||||
|
amount: -4.50,
|
||||||
|
date: DateTime.now().subtract(const Duration(hours: 3)),
|
||||||
|
category: 'Food & Drink',
|
||||||
|
),
|
||||||
|
Transaction(
|
||||||
|
id: 't2',
|
||||||
|
description: 'Salary Deposit',
|
||||||
|
amount: 2500.00,
|
||||||
|
date: DateTime.now().subtract(const Duration(days: 2)),
|
||||||
|
category: 'Income',
|
||||||
|
),
|
||||||
|
Transaction(
|
||||||
|
id: 't3',
|
||||||
|
description: 'Electric Bill',
|
||||||
|
amount: -85.75,
|
||||||
|
date: DateTime.now().subtract(const Duration(days: 3)),
|
||||||
|
category: 'Utilities',
|
||||||
|
),
|
||||||
|
Transaction(
|
||||||
|
id: 't4',
|
||||||
|
description: 'Amazon Purchase',
|
||||||
|
amount: -32.50,
|
||||||
|
date: DateTime.now().subtract(const Duration(days: 5)),
|
||||||
|
category: 'Shopping',
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
automaticallyImplyLeading: false,
|
title: const Text('Dashboard'),
|
||||||
title: const Text('kMobile', style: TextStyle(color: Colors.blueAccent,
|
|
||||||
fontWeight: FontWeight.w500),),
|
|
||||||
actions: [
|
actions: [
|
||||||
// IconButton(
|
IconButton(
|
||||||
// icon: const Icon(Icons.notifications_outlined),
|
icon: const Icon(Icons.notifications_outlined),
|
||||||
// onPressed: () {
|
onPressed: () {
|
||||||
// // Navigate to notifications
|
// Navigate to notifications
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 10.0),
|
|
||||||
child: InkWell(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
onTap: (){
|
|
||||||
// Navigator.push(context, MaterialPageRoute(
|
|
||||||
// builder: (context) => const CustomerInfoScreen()));
|
|
||||||
},
|
},
|
||||||
child: const CircleAvatar(
|
|
||||||
backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image
|
|
||||||
radius: 20,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.logout),
|
||||||
|
onPressed: () {
|
||||||
|
_showLogoutConfirmation(context);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
// Implement refresh logic to fetch updated data
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
},
|
||||||
|
child: SingleChildScrollView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 16),
|
// Greeting section
|
||||||
|
BlocBuilder<AuthCubit, AuthState>(
|
||||||
// Account Info Card
|
builder: (context, state) {
|
||||||
Container(
|
if (state is Authenticated) {
|
||||||
padding: const EdgeInsets.all(16),
|
return Column(
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.blue[700],
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: const Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Text(
|
||||||
|
'Hello, ${state.user.username}',
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Welcome back to your banking app',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Container();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// Account cards section
|
||||||
|
const Text(
|
||||||
|
'Your Accounts',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
SizedBox(
|
||||||
|
height: 180,
|
||||||
|
child: ListView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: _accounts.length + 1, // +1 for the "Add Account" card
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index < _accounts.length) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 16.0),
|
||||||
|
child: AccountCard(account: _accounts[index]),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// "Add Account" card
|
||||||
|
return Container(
|
||||||
|
width: 300,
|
||||||
|
margin: const EdgeInsets.only(right: 16.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: Colors.grey[300]!),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text("Account Number: ", style:
|
Icon(
|
||||||
TextStyle(color: Colors.white, fontSize: 12)),
|
Icons.add_circle_outline,
|
||||||
Text("03000156789462302", style:
|
size: 40,
|
||||||
TextStyle(color: Colors.white, fontSize: 14)),
|
color: Theme.of(context).primaryColor,
|
||||||
Padding(
|
),
|
||||||
padding: EdgeInsets.only(left: 5.0),
|
const SizedBox(height: 8),
|
||||||
child: Icon(Symbols.keyboard_arrow_down, color: Colors.white),
|
Text(
|
||||||
|
'Add New Account',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 15),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text("₹ *****", style: TextStyle(color: Colors.white,
|
|
||||||
fontSize: 24, fontWeight: FontWeight.w700)),
|
|
||||||
Icon(Symbols.visibility_lock, color: Colors.white),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(height: 20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 18),
|
|
||||||
const Text('Quick Links', style: TextStyle(fontSize: 15),),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Quick Links
|
|
||||||
GridView.count(
|
|
||||||
crossAxisCount: 4,
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
children: [
|
|
||||||
_buildQuickLink(Symbols.id_card, "Customer \n Info", () {
|
|
||||||
Navigator.push(context, MaterialPageRoute(
|
|
||||||
builder: (context) => const CustomerInfoScreen()));;
|
|
||||||
}),
|
|
||||||
_buildQuickLink(Symbols.currency_rupee, "Quick \n Pay",
|
|
||||||
() => print("")),
|
|
||||||
_buildQuickLink(Symbols.send_money, "Fund \n Transfer",
|
|
||||||
() => print("")),
|
|
||||||
_buildQuickLink(Symbols.server_person, "Account \n Info",
|
|
||||||
() => print("")),
|
|
||||||
_buildQuickLink(Symbols.receipt_long, "Account \n History",
|
|
||||||
() => print("")),
|
|
||||||
_buildQuickLink(Symbols.checkbook, "Handle \n Cheque",
|
|
||||||
() => print("")),
|
|
||||||
_buildQuickLink(Icons.group, "Manage \n Beneficiary",
|
|
||||||
() => print("")),
|
|
||||||
_buildQuickLink(Symbols.support_agent, "Contact \n Us",
|
|
||||||
() => print("")),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
|
|
||||||
// Recent Transactions
|
|
||||||
const Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Text("Recent Transactions", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
|
||||||
),
|
|
||||||
...transactions.map((tx) => ListTile(
|
|
||||||
leading: Icon(tx['type'] == 'in' ? Symbols.call_received : Symbols.call_made, color: tx['type'] == 'in' ? Colors.green : Colors.red),
|
|
||||||
title: Text(tx['name']!),
|
|
||||||
subtitle: Text(tx['date']!),
|
|
||||||
trailing: Text(tx['amount']!),
|
|
||||||
)),
|
|
||||||
|
|
||||||
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
Widget _buildQuickLink(IconData icon, String label, VoidCallback onTap) {
|
const SizedBox(height: 32),
|
||||||
return InkWell(
|
|
||||||
|
// Quick Actions section
|
||||||
|
const Text(
|
||||||
|
'Quick Actions',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
_buildQuickActionButton(
|
||||||
|
icon: Icons.swap_horiz,
|
||||||
|
label: 'Transfer',
|
||||||
|
onTap: () {
|
||||||
|
// Navigate to transfer screen
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildQuickActionButton(
|
||||||
|
icon: Icons.payment,
|
||||||
|
label: 'Pay Bills',
|
||||||
|
onTap: () {
|
||||||
|
// Navigate to bill payment screen
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildQuickActionButton(
|
||||||
|
icon: Icons.qr_code_scanner,
|
||||||
|
label: 'Scan & Pay',
|
||||||
|
onTap: () {
|
||||||
|
// Navigate to QR code scanner
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildQuickActionButton(
|
||||||
|
icon: Icons.more_horiz,
|
||||||
|
label: 'More',
|
||||||
|
onTap: () {
|
||||||
|
// Show more options
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
|
// Recent Transactions section
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Recent Transactions',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Navigate to all transactions
|
||||||
|
},
|
||||||
|
child: const Text('See All'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
ListView.builder(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: _recentTransactions.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return TransactionListItem(
|
||||||
|
transaction: _recentTransactions[index],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
|
currentIndex: 0,
|
||||||
|
type: BottomNavigationBarType.fixed,
|
||||||
|
items: const [
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.dashboard),
|
||||||
|
label: 'Home',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.account_balance_wallet),
|
||||||
|
label: 'Accounts',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.show_chart),
|
||||||
|
label: 'Insights',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.person),
|
||||||
|
label: 'Profile',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onTap: (index) {
|
||||||
|
// Handle navigation
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildQuickActionButton({
|
||||||
|
required IconData icon,
|
||||||
|
required String label,
|
||||||
|
required VoidCallback onTap,
|
||||||
|
}) {
|
||||||
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, size: 30, color: Colors.blue[700]),
|
Container(
|
||||||
const SizedBox(height: 4),
|
padding: const EdgeInsets.all(12),
|
||||||
Text(label, textAlign: TextAlign.center, style: const TextStyle(fontSize: 12)),
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).primaryColor.withAlpha((0.1 * 255).toInt()),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
icon,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
size: 28,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showLogoutConfirmation(BuildContext context) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Logout'),
|
||||||
|
content: const Text('Are you sure you want to logout?'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('CANCEL'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
context.read<AuthCubit>().logout();
|
||||||
|
},
|
||||||
|
child: const Text('LOGOUT'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
212
pubspec.lock
212
pubspec.lock
@ -1,14 +1,6 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
args:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: args
|
|
||||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.7.0"
|
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -33,14 +25,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
chalkdart:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: chalkdart
|
|
||||||
sha256: "7ffc6bd39c81453fb9ba8dbce042a9c960219b75ea1c07196a7fa41c2fab9e86"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.5"
|
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -113,14 +97,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
file:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: file
|
|
||||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "7.0.1"
|
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -142,14 +118,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
flutter_plugin_android_lifecycle:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: flutter_plugin_android_lifecycle
|
|
||||||
sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.28"
|
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -198,14 +166,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
flutter_svg:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_svg
|
|
||||||
sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.0"
|
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -224,22 +184,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.0.3"
|
version: "8.0.3"
|
||||||
glob:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: glob
|
|
||||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.3"
|
|
||||||
http:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: http
|
|
||||||
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.0"
|
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -296,46 +240,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
local_auth:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: local_auth
|
|
||||||
sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.0"
|
|
||||||
local_auth_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: local_auth_android
|
|
||||||
sha256: "63ad7ca6396290626dc0cb34725a939e4cfe965d80d36112f08d49cf13a8136e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.49"
|
|
||||||
local_auth_darwin:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: local_auth_darwin
|
|
||||||
sha256: "630996cd7b7f28f5ab92432c4b35d055dd03a747bc319e5ffbb3c4806a3e50d2"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.4.3"
|
|
||||||
local_auth_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: local_auth_platform_interface
|
|
||||||
sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.10"
|
|
||||||
local_auth_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: local_auth_windows
|
|
||||||
sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.11"
|
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -352,14 +256,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.11.1"
|
||||||
material_symbols_icons:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: material_symbols_icons
|
|
||||||
sha256: d45b6c36c3effa8cb51b1afb8698107d5ff1f88fa4631428f34a8a01abc295d7
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.2815.0"
|
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -384,14 +280,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
path_parsing:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_parsing
|
|
||||||
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -404,10 +292,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
|
sha256: "0ca7359dad67fd7063cb2892ab0c0737b2daafd807cf1acecd62374c8fae6c12"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.17"
|
version: "2.2.16"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -440,14 +328,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
petitparser:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: petitparser
|
|
||||||
sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "6.1.0"
|
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -472,62 +352,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.4"
|
version: "6.1.4"
|
||||||
shared_preferences:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: shared_preferences
|
|
||||||
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.5.3"
|
|
||||||
shared_preferences_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_android
|
|
||||||
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.10"
|
|
||||||
shared_preferences_foundation:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_foundation
|
|
||||||
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.5.4"
|
|
||||||
shared_preferences_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_linux
|
|
||||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.1"
|
|
||||||
shared_preferences_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_platform_interface
|
|
||||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.1"
|
|
||||||
shared_preferences_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_web
|
|
||||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.3"
|
|
||||||
shared_preferences_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_windows
|
|
||||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.1"
|
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -589,30 +413,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
vector_graphics:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: vector_graphics
|
|
||||||
sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.18"
|
|
||||||
vector_graphics_codec:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: vector_graphics_codec
|
|
||||||
sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.13"
|
|
||||||
vector_graphics_compiler:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: vector_graphics_compiler
|
|
||||||
sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.16"
|
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -653,14 +453,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
xml:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: xml
|
|
||||||
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "6.5.0"
|
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.7.0 <4.0.0"
|
dart: ">=3.7.0 <4.0.0"
|
||||||
flutter: ">=3.27.0"
|
flutter: ">=3.27.0"
|
||||||
|
21
pubspec.yaml
21
pubspec.yaml
@ -42,10 +42,6 @@ dependencies:
|
|||||||
flutter_bloc: ^9.1.0
|
flutter_bloc: ^9.1.0
|
||||||
get_it: ^8.0.3
|
get_it: ^8.0.3
|
||||||
intl: any
|
intl: any
|
||||||
flutter_svg: ^2.1.0
|
|
||||||
local_auth: ^2.3.0
|
|
||||||
material_symbols_icons: ^4.2815.0
|
|
||||||
shared_preferences: ^2.5.3
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -70,9 +66,9 @@ flutter:
|
|||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
assets:
|
# assets:
|
||||||
- assets/images/kccb_logo.svg
|
# - images/a_dot_burr.jpeg
|
||||||
- assets/images/avatar.jpg
|
# - images/a_dot_ham.jpeg
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
@ -85,12 +81,11 @@ flutter:
|
|||||||
# "family" key with the font family name, and a "fonts" key with a
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
# list giving the asset and other descriptors for the font. For
|
# list giving the asset and other descriptors for the font. For
|
||||||
# example:
|
# example:
|
||||||
fonts:
|
# fonts:
|
||||||
- family: Rubik
|
# - family: Schyler
|
||||||
fonts:
|
# fonts:
|
||||||
- asset: assets/fonts/Rubik-Regular.ttf
|
# - asset: fonts/Schyler-Regular.ttf
|
||||||
- asset: assets/fonts/Rubik-Bold.ttf
|
# - asset: fonts/Schyler-Italic.ttf
|
||||||
weight: 700
|
|
||||||
# style: italic
|
# style: italic
|
||||||
# - family: Trajan Pro
|
# - family: Trajan Pro
|
||||||
# fonts:
|
# fonts:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user