ironing out erp bugs

This commit is contained in:
2025-09-27 01:09:05 +02:00
parent cbabff4c10
commit f1e2327732
41 changed files with 1440 additions and 516 deletions

View File

@@ -1,5 +1,8 @@
plugins {
id("com.android.application")
// START: FlutterFire Configuration
id("com.google.gms.google-services")
// END: FlutterFire Configuration
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
@@ -24,7 +27,8 @@ android {
applicationId = "zw.co.qantra.qpay"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
// minSdk = flutter.minSdkVersion
minSdk = 23
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName

View File

@@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "77433712483",
"project_id": "peak-c376b",
"storage_bucket": "peak-c376b.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:77433712483:android:c6508ec8c6a23889ab6d56",
"android_client_info": {
"package_name": "zw.co.qantra.qpay"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyARyIWVumzj4UWNwplKZRxxVYmw_H6hBKI"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -19,6 +19,9 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.0" apply false
// START: FlutterFire Configuration
id("com.google.gms.google-services") version("4.3.15") apply false
// END: FlutterFire Configuration
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
}

View File

@@ -1,7 +1,8 @@
; BASE_URL=https://peakapi.qantra.co.zw/api
BASE_URL=http://192.168.100.8:6950/api
BASE_URL=http://192.168.100.26:6950/api
; BASE_URL=http://192.168.1.164:6950/api
; BASE_URL=http://192.168.120.160:6950/api
; BASE_URL=http://10.10.2.92:6950/api
; BASE_URL=http://172.20.5.105:6950/api
; BASE_URL=http://10.10.3.92:6950/api
CLIENTID=77433712483-ng7pntvcpf6tnjccriuqm8dbna8vvp3b.apps.googleusercontent.com

BIN
assets/login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
assets/phone.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
assets/phone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
assets/social.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
assets/social.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

1
firebase.json Normal file
View File

@@ -0,0 +1 @@
{"flutter":{"platforms":{"dart":{"lib/firebase_options.dart":{"projectId":"peak-c376b","configurations":{"android":"1:77433712483:android:c6508ec8c6a23889ab6d56","ios":"1:77433712483:ios:f21e81701d8a2740ab6d56","macos":"1:77433712483:ios:f21e81701d8a2740ab6d56","web":"1:77433712483:web:89d62f69ed8ffb09ab6d56","windows":"1:77433712483:web:03b1df2872cd9723ab6d56"}}},"android":{"default":{"projectId":"peak-c376b","appId":"1:77433712483:android:c6508ec8c6a23889ab6d56","fileOutput":"android/app/google-services.json"}}}}}

13
firepit-log.txt Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
class AbstractController extends ChangeNotifier {
late BuildContext context;
void showErrorSnackBar(String? message) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message.toString()),
behavior: SnackBarBehavior.floating,
backgroundColor: Colors.deepOrange,
),
);
});
}
}

89
lib/firebase_options.dart Normal file
View File

@@ -0,0 +1,89 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
return macos;
case TargetPlatform.windows:
return windows;
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSyDqcGjTQTrZjhFoBvbhwsOuDaxvA4p8Ch4',
appId: '1:77433712483:web:89d62f69ed8ffb09ab6d56',
messagingSenderId: '77433712483',
projectId: 'peak-c376b',
authDomain: 'peak-c376b.firebaseapp.com',
storageBucket: 'peak-c376b.firebasestorage.app',
measurementId: 'G-B94Y2DVT96',
);
static const FirebaseOptions macos = FirebaseOptions(
apiKey: 'AIzaSyD3XsySzfF9Tp84jbAc_T0AwI-RSDRawmI',
appId: '1:77433712483:ios:f21e81701d8a2740ab6d56',
messagingSenderId: '77433712483',
projectId: 'peak-c376b',
storageBucket: 'peak-c376b.firebasestorage.app',
iosBundleId: 'zw.co.qantra.qpay',
);
static const FirebaseOptions windows = FirebaseOptions(
apiKey: 'AIzaSyDqcGjTQTrZjhFoBvbhwsOuDaxvA4p8Ch4',
appId: '1:77433712483:web:03b1df2872cd9723ab6d56',
messagingSenderId: '77433712483',
projectId: 'peak-c376b',
authDomain: 'peak-c376b.firebaseapp.com',
storageBucket: 'peak-c376b.firebasestorage.app',
measurementId: 'G-DMD0HQ4W18',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyD3XsySzfF9Tp84jbAc_T0AwI-RSDRawmI',
appId: '1:77433712483:ios:f21e81701d8a2740ab6d56',
messagingSenderId: '77433712483',
projectId: 'peak-c376b',
storageBucket: 'peak-c376b.firebasestorage.app',
iosBundleId: 'zw.co.qantra.qpay',
);
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyARyIWVumzj4UWNwplKZRxxVYmw_H6hBKI',
appId: '1:77433712483:android:c6508ec8c6a23889ab6d56',
messagingSenderId: '77433712483',
projectId: 'peak-c376b',
storageBucket: 'peak-c376b.firebasestorage.app',
);
}

View File

@@ -17,7 +17,7 @@ class Http {
LogInterceptor(
request: true,
responseBody: false,
requestBody: false,
requestBody: true,
error: true,
),
);

View File

@@ -3,9 +3,12 @@ import 'package:provider/provider.dart';
import 'package:go_router/go_router.dart';
import 'package:qpay/screens/confirm/confirm_screen.dart';
import 'package:qpay/screens/gateway/gateway_screen.dart';
import 'package:qpay/screens/onboarding/complete_screen.dart';
import 'package:qpay/screens/onboarding/landing/landing_screen.dart';
import 'package:qpay/screens/onboarding/login/login_screen.dart';
import 'package:qpay/screens/onboarding/onboarding_controller.dart';
import 'package:qpay/screens/onboarding/password/password_screen.dart';
import 'package:qpay/screens/onboarding/phone/phone_screen.dart';
import 'package:qpay/screens/onboarding/register/register_screen.dart';
import 'package:qpay/screens/onboarding/verify/verify_screen.dart';
import 'package:qpay/screens/pay/pay_screen.dart';
@@ -18,15 +21,25 @@ import 'screens/profile_screen.dart';
import 'screens/splash_screen.dart';
import 'theme/app_theme.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
const String testEnv = "assets/.env";
const String liveEnv = "assets/.env";
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
await dotenv.load(fileName: testEnv);
runApp(
ChangeNotifierProvider(
create: (context) => TransactionController(),
MultiProvider(
providers: [
ChangeNotifierProvider<TransactionController>(create: (_) => TransactionController()),
ChangeNotifierProvider<OnboardingController>(create: (_) => OnboardingController()),
],
child: const MyApp(),
),
);
@@ -124,6 +137,10 @@ class _MyAppState extends State<MyApp> {
path: '/onboarding/register',
builder: (context, state) => const RegisterScreen(),
),
GoRoute(
path: '/onboarding/phone',
builder: (context, state) => const PhoneScreen(),
),
GoRoute(
path: '/onboarding/verify',
builder: (context, state) => const VerifyScreen(),
@@ -136,6 +153,10 @@ class _MyAppState extends State<MyApp> {
path: '/onboarding/login',
builder: (context, state) => const LoginScreen(),
),
GoRoute(
path: '/onboarding/complete',
builder: (context, state) => const CompleteScreen(),
),
],
),
],

View File

@@ -280,7 +280,7 @@ class HomeController extends ChangeNotifier {
description: 'USD Wallet',
image: 'united-states.png',
currency: 'USD',
balance: '4.02',
balance: '47655.02',
),
Account(
name: 'Vusumuzi Khoza',

View File

@@ -21,13 +21,12 @@ class _HomeScreenState extends State<HomeScreen>
late AnimationController _controller;
late Animation<AlignmentGeometry> _alignAnimation;
late TransactionController transactionController;
late bool _loading = true;
late SharedPreferences prefs;
late HomeController homeController;
late final router = GoRouter.of(context);
late String initials;
Timer? _loadingTimer;
bool get _isLoggedIn => false;
bool _isLoggedIn = false;
String _selectedFilter = 'All';
final List<Animation<Offset>> _animations = [];
@@ -36,15 +35,6 @@ class _HomeScreenState extends State<HomeScreen>
@override
void initState() {
super.initState();
router.routerDelegate.addListener(_onRouteChanged);
transactionController = Provider.of<TransactionController>(
context,
listen: false,
);
// Reset the transaction controller model
transactionController.model = TransactionModel();
_controller = AnimationController(
duration: const Duration(milliseconds: 600),
@@ -69,18 +59,19 @@ class _HomeScreenState extends State<HomeScreen>
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));
setupDataAndTransitions();
// Simulate a delay to mimic loading state
_loadingTimer = Timer(const Duration(seconds: 1), () {
if (mounted) {
setState(() {
_loading = false;
});
}
});
}
Future<void> setupDataAndTransitions() async {
router.routerDelegate.addListener(_onRouteChanged);
transactionController = Provider.of<TransactionController>(
context,
listen: false,
);
// Reset the transaction controller model
transactionController.model = TransactionModel();
homeController = HomeController(context);
await homeController.getAccounts();
await homeController.getCategories();
@@ -103,8 +94,11 @@ class _HomeScreenState extends State<HomeScreen>
prefs = await SharedPreferences.getInstance();
if (prefs.getString("userId") == null) {
prefs.setString("userId", Uuid().v4());
if (prefs.getString("token") != null) {
setState(() {
_isLoggedIn = true;
initials = prefs.getString("initials")!;
});
}
await homeController.getTransactions(prefs.getString("userId")!);
@@ -126,16 +120,21 @@ class _HomeScreenState extends State<HomeScreen>
);
}
void _onRouteChanged() {
void _onRouteChanged() async {
final location = router.routerDelegate.currentConfiguration.uri.toString();
if (location.startsWith('/home')) {
setupDataAndTransitions();
prefs = await SharedPreferences.getInstance();
if (prefs.getString("userId") == null) {
prefs.setString("userId", Uuid().v4());
}
await homeController.getTransactions(prefs.getString("userId")!);
}
}
@override
void dispose() {
_loadingTimer?.cancel();
_controller.dispose();
homeController.dispose();
router.routerDelegate.removeListener(_onRouteChanged);
@@ -159,10 +158,12 @@ class _HomeScreenState extends State<HomeScreen>
surfaceTintColor: Colors.transparent,
floating: true,
title: Image(image: AssetImage('assets/peak.png'), width: 85),
leading: IconButton(
leading: _isLoggedIn ? Container(
padding: const EdgeInsets.only(left: 10.0),
child: IconButton(
icon: Container(
width: 32,
height: 32,
width: 52,
height: 52,
decoration: BoxDecoration(
border: Border.all(color: Colors.black87.withAlpha(20)),
shape: BoxShape.circle,
@@ -182,7 +183,7 @@ class _HomeScreenState extends State<HomeScreen>
),
child: Center(
child: Text(
'VK',
initials,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
@@ -192,12 +193,48 @@ class _HomeScreenState extends State<HomeScreen>
),
onPressed: () {},
),
// actions: [
// IconButton(
// icon: const Icon(Icons.notifications_outlined),
// onPressed: () {},
// ),
// ],
) : SizedBox(),
actions: [
if(_isLoggedIn)
IconButton(
icon: const Icon(Icons.exit_to_app_sharp),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext dialogContext) {
return AlertDialog(
title: const Text('Confirm Logout'),
content: const Text('Are you sure you want to log out?'),
actions: <Widget>[
TextButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(dialogContext).pop(); // Dismiss the dialog
},
),
TextButton(
child: const Text('Logout'),
onPressed: () {
Navigator.of(dialogContext).pop(); // Dismiss the dialog
prefs.remove("token");
setState(() {
_isLoggedIn = false;
});
// It's generally not recommended to call initState directly.
// Consider moving the logic from initState that needs to be rerun
// into a separate method and call that method here.
// For example, if you have a method like _reloadData(), call it here.
// _reloadData();
setupDataAndTransitions(); // Or specific parts of it if not all is needed
},
),
],
);
},
);
},
),
],
),
SliverToBoxAdapter(
child: Padding(
@@ -209,7 +246,7 @@ class _HomeScreenState extends State<HomeScreen>
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!_isLoggedIn) _buildLogin(homeController),
if (_isLoggedIn) _buildAccounts(homeController),
// if (_isLoggedIn) _buildAccounts(homeController),
_buildFilterChips(homeController),
SizedBox(height: 10),
Column(
@@ -361,7 +398,7 @@ class _HomeScreenState extends State<HomeScreen>
// context.push("/wallet");
},
child: Container(
width: 150,
width: 170,
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 12,
@@ -388,11 +425,11 @@ class _HomeScreenState extends State<HomeScreen>
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset('assets/${e.image}', width: 20),
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Image.asset('assets/${e.image}', width: 20),
SizedBox(width: 10),
Text(
e.currency,
style: TextStyle(
@@ -402,12 +439,15 @@ class _HomeScreenState extends State<HomeScreen>
),
),
SizedBox(width: 5),
Text(
Expanded(
child: Text(
e.balance,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 22,
color: Colors.black,
overflow: TextOverflow.ellipsis,
),
),
),
],

View File

@@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:qpay/screens/onboarding/login/login_controller.dart';
class CompleteScreen extends StatefulWidget {
const CompleteScreen({super.key});
@override
State<CompleteScreen> createState() => _CompleteScreenState();
}
class _CompleteScreenState extends State<CompleteScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(25),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: 75),
Text(
'Registration Complete',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
SizedBox(height: 5),
Text(
'Thank you for registering with Peak payments',
style: TextStyle(fontSize: 18),
textAlign: TextAlign.center,
),
Image.asset('assets/landing.png', width: 300),
Text(
'Tap Login to get started',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 10),
],
),
),
),
bottomNavigationBar: SizedBox(
height: 130,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
context.go('/onboarding/login');
},
child: Text('Go to Login', style: TextStyle(fontSize: 16)),
),
),
],
),
),
SizedBox(height: 5),
],
),
),
);
}
}

View File

@@ -54,6 +54,8 @@ class LoginController extends ChangeNotifier {
prefs.setString('firstName', response['firstName']);
prefs.setString('lastName', response['lastName']);
prefs.setString('phone', response['phone']);
prefs.setString('userId', response['uuid']);
prefs.setString('initials', response['firstName'].substring(0, 1) + response['lastName'].substring(0, 1));
return model;
} else {
@@ -64,7 +66,7 @@ class LoginController extends ChangeNotifier {
}
} on DioException catch (e, s) {
logger.e(s);
_showErrorSnackBar("Network error. Please try again or contact support");
_showErrorSnackBar(e.response?.data['errors'][0] ?? "Unknown error");
} catch (e, s) {
logger.e(s);
_showErrorSnackBar(

View File

@@ -1,6 +1,12 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:qpay/screens/onboarding/login/login_controller.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@@ -10,9 +16,13 @@ class LoginScreen extends StatefulWidget {
}
class _LoginScreenState extends State<LoginScreen> {
late SharedPreferences prefs;
final _formKey = GlobalKey<FormState>();
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
bool _loading = false;
List<String> scopes = <String>[
'https://www.googleapis.com/auth/contacts.readonly',
];
late LoginController _loginController;
@@ -22,6 +32,65 @@ class _LoginScreenState extends State<LoginScreen> {
_loginController = LoginController(context);
}
Future<void> signInWithGoogle() async {
final GoogleSignIn googleSignIn = GoogleSignIn.instance;
unawaited(googleSignIn.initialize(serverClientId: dotenv.env['CLIENTID']));
if (GoogleSignIn.instance.supportsAuthenticate()){
unawaited(GoogleSignIn.instance.authenticate().then((value) async {
print(value);
saveUser(value);
await _loginController.login(value.email, value.id);
context.go('/');
googleSignIn.authenticationEvents
.listen(_handleAuthenticationEvent)
.onError(_handleAuthenticationError);
}));
}
}
void saveUser(GoogleSignInAccount user) async {
final Map<String, dynamic> data = <String, dynamic>{
'displayName': user.displayName,
'email': user.email,
'id': user.id,
'photoUrl': user.photoUrl,
};
prefs = await SharedPreferences.getInstance();
if (prefs.getString("googleUser") == null) {
prefs.setString("googleUser", jsonEncode(data));
}
}
Future<void> _handleAuthenticationEvent(
GoogleSignInAuthenticationEvent event,
) async {
// #docregion CheckAuthorization
final GoogleSignInAccount? user = // ...
// #enddocregion CheckAuthorization
switch (event) {
GoogleSignInAuthenticationEventSignIn() => event.user,
GoogleSignInAuthenticationEventSignOut() => null,
};
// Check for existing authorization.
// #docregion CheckAuthorization
final GoogleSignInClientAuthorization? authorization = await user
?.authorizationClient
.authorizationForScopes(scopes);
// #enddocregion CheckAuthorization
}
Future<void> _handleAuthenticationError(Object e) async {
print(e.toString());
}
@override
void dispose() {
_loginController.dispose();
@@ -56,55 +125,15 @@ class _LoginScreenState extends State<LoginScreen> {
textAlign: TextAlign.center,
),
SizedBox(height: 40),
Image.asset('assets/password.png', width: 300),
Text(
'Tap on your preferred social media platform to get started',
style: TextStyle(fontSize: 18),
textAlign: TextAlign.center,
),
// Username Field
TextFormField(
controller: _usernameController,
decoration: InputDecoration(
labelText: 'Phone Number',
prefixIcon: Icon(Icons.person_outline),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a phone number';
}
return null;
},
),
SizedBox(height: 20),
// Password Field
TextFormField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock_outline),
suffixIcon: Icon(Icons.visibility_outlined),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a password';
}
return null;
},
),
SizedBox(height: 20),
// Forgot Password Link
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: () {
// TODO: Add forgot password logic
},
child: Text(
'Forgot Password?',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontSize: 14,
),
),
),
),
SizedBox(height: 40),
// Divider with "or" text
Row(
children: [
@@ -112,11 +141,12 @@ class _LoginScreenState extends State<LoginScreen> {
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text(
'or continue with',
'choose from the options below to continue',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
),
textAlign: TextAlign.center,
),
),
Expanded(child: Divider(color: Colors.grey.shade300)),
@@ -128,8 +158,9 @@ class _LoginScreenState extends State<LoginScreen> {
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () {
// TODO: Add Google login logic
onPressed: () async {
await signInWithGoogle();
},
icon: Image.asset(
'assets/google.png',
@@ -146,27 +177,6 @@ class _LoginScreenState extends State<LoginScreen> {
),
),
),
SizedBox(width: 16),
Expanded(
child: OutlinedButton.icon(
onPressed: () {
// TODO: Add Apple login logic
},
icon: Image.asset(
'assets/apple.png',
width: 20,
height: 20,
),
label: Text('Apple', style: TextStyle(fontSize: 16)),
style: OutlinedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 16),
side: BorderSide(color: Colors.grey.shade300),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
),
SizedBox(height: 40),
@@ -179,43 +189,6 @@ class _LoginScreenState extends State<LoginScreen> {
height: 130,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () async {
// TODO: Add login logic
if (_formKey.currentState!.validate()) {
LoginScreenModel model = await _loginController.login(
_usernameController.text,
_passwordController.text,
);
if (model.status == 'SUCCESS') {
context.go('/');
}
}
},
child: Text('Login', style: TextStyle(fontSize: 16)),
),
),
SizedBox(width: 16),
Expanded(
child: TextButton(
onPressed: () {
context.go('/onboarding/register');
},
child: Text(
'Create Account',
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
SizedBox(height: 5),
TextButton(
onPressed: () {

View File

@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:qpay/http/http.dart';
class OnboardingModel {
String? phone;
String? workflowId;
String? username;
GoogleSignInAccount? googleUser;
bool isLoading = false;
}
class OnboardingController extends ChangeNotifier {
OnboardingModel model = OnboardingModel();
void resetState() {
model = OnboardingModel();
}
void updateModel(Map mapModel) {
model.workflowId = mapModel['workflowId'];
model.phone = mapModel['phone'];
model.username = mapModel['username'];
notifyListeners();
}
updatePhone(String phone) {
model.phone = phone;
notifyListeners();
}
updateGoogleUser(GoogleSignInAccount googleUser) {
model.googleUser = googleUser;
notifyListeners();
}
}

View File

@@ -0,0 +1,73 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:qpay/abstracts/AbstractController.dart';
import 'package:qpay/http/http.dart';
import '../onboarding_controller.dart';
class PhoneModel {
bool isLoading = false;
String? errorMessage;
String? status = 'failed';
}
class PhoneController extends AbstractController {
PhoneModel model = PhoneModel();
@override
BuildContext context;
late OnboardingController onboardingController;
final Http http = Http();
PhoneController(this.context) {
onboardingController = Provider.of<OnboardingController>(
context,
listen: false,
);
}
void updatePhone(String phone) {
onboardingController.updatePhone(phone);
notifyListeners();
}
Future<void> register() async {
Map user = {
"username": onboardingController.model.googleUser?.email,
"email": onboardingController.model.googleUser?.email,
"firstName": onboardingController.model.googleUser?.displayName?.split(" ").first,
"lastName": onboardingController.model.googleUser?.displayName?.split(" ").last,
"password": onboardingController.model.googleUser?.id,
"photoUrl": onboardingController.model.googleUser?.photoUrl,
"phone": onboardingController.model.phone
};
try {
model.isLoading = true;
Map response = await http.post(
'/auth/register',
user
);
model.status = response['state'];
if(response['state'] == 'failed') {
model.errorMessage = response['message'];
showErrorSnackBar(model.errorMessage);
}
Map body = response['body'];
body['workflowId'] = response['workflowId'];
onboardingController.updateModel(body);
} catch (e) {
logger.e(e);
showErrorSnackBar(
"Problem during registration, please try again or contact support?",
);
}
model.isLoading = false;
notifyListeners();
}
}

View File

@@ -0,0 +1,270 @@
import 'package:flutter/material.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:go_router/go_router.dart';
import 'package:qpay/screens/onboarding/phone/phone_controller.dart';
import 'package:skeletonizer/skeletonizer.dart';
class PhoneScreen extends StatefulWidget {
const PhoneScreen({super.key});
@override
State<PhoneScreen> createState() => _PhoneScreenState();
}
class _PhoneScreenState extends State<PhoneScreen> {
late PhoneController phoneController;
final _formKey = GlobalKey<FormState>();
final TextEditingController _phoneController = TextEditingController();
String selectedCountryCode = '+263'; // Default to ZW
// Common country codes with flags and names
final List<Map<String, String>> countryCodes = [
{'code': '+263', 'name': 'ZW', 'flag': '🇿🇼'},
{'code': '+27', 'name': 'ZA', 'flag': '🇿🇦'},
{'code': '+1', 'name': 'US', 'flag': '🇺🇸'},
{'code': '+44', 'name': 'UK', 'flag': '🇬🇧'},
{'code': '+61', 'name': 'AU', 'flag': '🇦🇺'},
{'code': '+91', 'name': 'IN', 'flag': '🇮🇳'},
{'code': '+86', 'name': 'CN', 'flag': '🇨🇳'},
{'code': '+81', 'name': 'JP', 'flag': '🇯🇵'},
{'code': '+49', 'name': 'DE', 'flag': '🇩🇪'},
{'code': '+33', 'name': 'FR', 'flag': '🇫🇷'},
{'code': '+39', 'name': 'IT', 'flag': '🇮🇹'},
{'code': '+34', 'name': 'ES', 'flag': '🇪🇸'},
{'code': '+7', 'name': 'RU', 'flag': '🇷🇺'},
{'code': '+55', 'name': 'BR', 'flag': '🇧🇷'},
{'code': '+52', 'name': 'MX', 'flag': '🇲🇽'},
{'code': '+971', 'name': 'AE', 'flag': '🇦🇪'},
{'code': '+966', 'name': 'SA', 'flag': '🇸🇦'},
{'code': '+234', 'name': 'NG', 'flag': '🇳🇬'},
{'code': '+254', 'name': 'KE', 'flag': '🇰🇪'},
{'code': '+233', 'name': 'GH', 'flag': '🇬🇭'},
{'code': '+256', 'name': 'UG', 'flag': '🇺🇬'},
];
@override
void initState() {
super.initState();
phoneController = PhoneController(context);
}
Future<void> updatePhoneNumber(String phoneNumber) async {
String existingPhone = phoneNumber;
if (existingPhone.isNotEmpty) {
// Try to extract country code from existing phone number
for (var country in countryCodes) {
if (existingPhone.startsWith(country['code']!)) {
setState(() {
selectedCountryCode = country['code']!;
});
_phoneController.text = existingPhone.substring(
country['code']!.length,
);
break;
}
}
// If no country code found, use default and set the full number
if (_phoneController.text.isEmpty) {
_phoneController.text = existingPhone;
}
}
}
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: phoneController,
builder: (context, child) {
return Scaffold(
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(25.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(height: 75),
Text(
'Verify Your Device',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Theme
.of(context)
.colorScheme
.primary,
),
textAlign: TextAlign.center,
),
SizedBox(height: 10),
Text(
'We will send you a verification code to this number',
style: TextStyle(fontSize: 18),
textAlign: TextAlign.center,
),
Image.asset('assets/phone.png', width: 300),
SizedBox(height: 20),
_buildPhoneField(),
SizedBox(height: 30),
],
),
),
),
),
bottomNavigationBar: SizedBox(
height: 130,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Skeletonizer(
enabled: phoneController.model.isLoading,
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
String fullPhoneNumber =
selectedCountryCode + _phoneController.text;
phoneController.updatePhone(fullPhoneNumber);
await phoneController.register();
if(phoneController.model.status != 'failed') {
context.go('/onboarding/verify');
}
}
},
child: Text('Send Code', style: TextStyle(fontSize: 16)),
),
),
SizedBox(width: 16),
Expanded(
child: TextButton(
onPressed: () {
context.go('/onboarding/login');
},
child: Text(
'Go to Login',
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
),
SizedBox(height: 5),
TextButton(
onPressed: () {
context.go('/');
},
child: Text('Continue as guest?', style: TextStyle(fontSize: 14)),
),
],
),
),
);
}
);
}
Widget _buildPhoneField() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// Country code dropdown
Container(
width: 100,
height: 55,
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).colorScheme.primary.withOpacity(0.2),
),
borderRadius: BorderRadius.circular(12),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: selectedCountryCode,
isExpanded: true,
icon: const Icon(Icons.arrow_drop_down),
padding: const EdgeInsets.symmetric(horizontal: 8),
items:
countryCodes.map((country) {
return DropdownMenuItem<String>(
value: country['code'],
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(country['flag']!),
const SizedBox(width: 4),
Text(
country['code']!,
style: const TextStyle(fontSize: 16),
),
],
),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
setState(() {
selectedCountryCode = newValue;
});
}
},
),
),
),
const SizedBox(width: 8),
// Phone number field
Expanded(
child: TextFormField(
controller: _phoneController,
keyboardType: TextInputType.numberWithOptions(decimal: true),
decoration: InputDecoration(
hintText: 'Enter phone number',
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Theme.of(context).colorScheme.secondary,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.2),
),
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a phone number';
}
// Remove any non-digit characters for validation
String digitsOnly = value.replaceAll(RegExp(r'[^\d]'), '');
if (digitsOnly.length < 7) {
return 'Phone number must be at least 7 digits';
}
if (digitsOnly.length > 15) {
return 'Phone number is too long';
}
return null;
},
),
),
],
),
],
);
}
}

View File

@@ -1,5 +1,12 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:provider/provider.dart';
import 'package:qpay/screens/onboarding/onboarding_controller.dart';
import 'package:skeletonizer/skeletonizer.dart';
class RegisterScreen extends StatefulWidget {
const RegisterScreen({super.key});
@@ -9,8 +16,69 @@ class RegisterScreen extends StatefulWidget {
}
class _RegisterScreenState extends State<RegisterScreen> {
late OnboardingController onboardingController;
bool _loading = false;
List<String> scopes = <String>[
'https://www.googleapis.com/auth/contacts.readonly',
];
@override
void initState() {
super.initState();
onboardingController = Provider.of<OnboardingController>(
context,
listen: false,
);
// onboardingController.resetState();
}
void signInWithGoogle() {
final GoogleSignIn googleSignIn = GoogleSignIn.instance;
unawaited(googleSignIn.initialize(serverClientId: dotenv.env['CLIENTID']));
if (GoogleSignIn.instance.supportsAuthenticate()){
unawaited(GoogleSignIn.instance.authenticate().then((value) {
print(value);
onboardingController.updateGoogleUser(value);
googleSignIn.authenticationEvents
.listen(_handleAuthenticationEvent)
.onError(_handleAuthenticationError);
}));
}
}
Future<void> _handleAuthenticationEvent(
GoogleSignInAuthenticationEvent event,
) async {
// #docregion CheckAuthorization
final GoogleSignInAccount? user = // ...
// #enddocregion CheckAuthorization
switch (event) {
GoogleSignInAuthenticationEventSignIn() => event.user,
GoogleSignInAuthenticationEventSignOut() => null,
};
// Check for existing authorization.
// #docregion CheckAuthorization
final GoogleSignInClientAuthorization? authorization = await user
?.authorizationClient
.authorizationForScopes(scopes);
// #enddocregion CheckAuthorization
}
Future<void> _handleAuthenticationError(Object e) async {
print(e.toString());
}
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: onboardingController,
builder: (context, child) {
return Scaffold(
body: SingleChildScrollView(
child: Padding(
@@ -30,80 +98,43 @@ class _RegisterScreenState extends State<RegisterScreen> {
),
SizedBox(height: 5),
Text(
'Join Peak and start making fast payments',
"Let's start by getting some of your details online to get you started",
style: TextStyle(fontSize: 18),
textAlign: TextAlign.center,
),
SizedBox(height: 40),
// First Name Field
TextField(
decoration: InputDecoration(
labelText: 'First Name',
prefixIcon: Icon(Icons.person_outline),
),
),
Image.asset('assets/social.png', width: 300),
SizedBox(height: 20),
// Last Name Field
TextField(
decoration: InputDecoration(
labelText: 'Last Name',
prefixIcon: Icon(Icons.person_outline),
),
),
SizedBox(height: 20),
// Phone Field
TextField(
decoration: InputDecoration(
labelText: 'Phone Number',
prefixIcon: Icon(Icons.phone_outlined),
),
keyboardType: TextInputType.phone,
),
SizedBox(height: 20),
// Email Field
TextField(
decoration: InputDecoration(
labelText: 'Email Address',
prefixIcon: Icon(Icons.email_outlined),
),
keyboardType: TextInputType.emailAddress,
),
SizedBox(height: 20),
// Password Field
TextField(
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock_outline),
suffixIcon: Icon(Icons.visibility_outlined),
),
obscureText: true,
),
SizedBox(height: 40),
// Divider with "or" text
// Social Login Buttons
if(onboardingController.model.googleUser == null)
Column(
children: [
Row(
children: [
Expanded(child: Divider(color: Colors.grey.shade300)),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text(
'or continue with',
'Choose from the options below to continue',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
),
textAlign: TextAlign.center,
),
),
Expanded(child: Divider(color: Colors.grey.shade300)),
],
),
SizedBox(height: 30),
// Social Login Buttons
Row(
SizedBox(height: 20),
Skeletonizer(
enabled: _loading,
child: Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () {
// TODO: Add Google login logic
signInWithGoogle();
},
icon: Image.asset(
'assets/google.png',
@@ -121,29 +152,33 @@ class _RegisterScreenState extends State<RegisterScreen> {
),
),
SizedBox(width: 16),
Expanded(
child: OutlinedButton.icon(
onPressed: () {
// TODO: Add Apple login logic
},
icon: Image.asset(
'assets/apple.png',
width: 20,
height: 20,
),
label: Text('Apple', style: TextStyle(fontSize: 16)),
style: OutlinedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 16),
side: BorderSide(color: Colors.grey.shade300),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
],
),
),
],
),
if(onboardingController.model.googleUser != null)
Column(
children: [
SizedBox(
width: 100,
child: Divider(
color: Colors.grey.shade300,
thickness: 1,
),
),
SizedBox(height: 20,),
Text(
"Welcome, ${onboardingController.model.googleUser?.displayName}",
style: TextStyle(fontSize: 25),
textAlign: TextAlign.center,
),
SizedBox(height: 10,),
Text("Tap Proceed to continue", style: TextStyle(fontSize: 18),)
],
),
SizedBox(height: 40),
],
),
),
@@ -159,9 +194,30 @@ class _RegisterScreenState extends State<RegisterScreen> {
Expanded(
child: ElevatedButton(
onPressed: () {
context.go('/onboarding/verify');
if (onboardingController.model.googleUser == null) {
showDialog(
context: context,
builder: (BuildContext dialogContext) {
return AlertDialog(
title: const Text('Sign-In Required'),
content: const Text(
"Please sign in with one of the social media platforms to proceed."),
actions: <Widget>[
TextButton(
child: const Text('OK'),
onPressed: () {
Navigator.of(dialogContext).pop(); // Close the dialog
},
child: Text('Submit', style: TextStyle(fontSize: 16)),
),
],
);
},
);
} else {
context.go('/onboarding/phone');
}
},
child: Text('Proceed', style: TextStyle(fontSize: 16)),
),
),
SizedBox(width: 16),
@@ -191,4 +247,6 @@ class _RegisterScreenState extends State<RegisterScreen> {
),
);
}
);
}
}

View File

@@ -0,0 +1,95 @@
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
import 'package:qpay/abstracts/AbstractController.dart';
import 'package:qpay/http/http.dart';
import 'package:qpay/screens/onboarding/onboarding_controller.dart';
class VerifyModel {
String? code;
bool isLoading = false;
String? errorMessage;
String? status = 'failed';
}
class VerifyController extends AbstractController {
late OnboardingController onboardingController;
final Http http = Http();
BuildContext context;
VerifyModel model = VerifyModel();
VerifyController(this.context){
onboardingController = Provider.of<OnboardingController>(
context,
listen: false,
);
}
void updateCode(String code) {
model.code = code;
notifyListeners();
}
Future<void> verifyOtp () async {
Map user = {
"username": onboardingController.model.phone,
"otpCode": model.code,
"otpType": "REGISTRATION",
"workflowId": onboardingController.model.workflowId,
};
try {
model.isLoading = true;
notifyListeners();
Map response = await http.post(
'/auth/verify',
user
);
model.status = response['state'];
if(response['state'] == 'failed') {
model.errorMessage = response['message'];
showErrorSnackBar(model.errorMessage);
}
} catch (e) {
logger.e(e);
showErrorSnackBar(
"Problem during registration, please try again or contact support?",
);
}
model.isLoading = false;
notifyListeners();
}
Future<void> resendOtp () async {
Map user = {
"username": onboardingController.model.phone,
"phone": onboardingController.model.phone,
"workflowId": onboardingController.model.workflowId,
};
try {
model.isLoading = true;
notifyListeners();
Map response = await http.post(
'/auth/resend',
user
);
model.status = response['state'];
if(response['state'] == 'failed') {
model.errorMessage = response['message'];
showErrorSnackBar(model.errorMessage);
}
} catch (e) {
logger.e(e);
showErrorSnackBar(
"Problem during registration, please try again or contact support?",
);
}
model.isLoading = false;
notifyListeners();
}
}

View File

@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:qpay/screens/onboarding/verify/verify_controller.dart';
import 'package:skeletonizer/skeletonizer.dart';
class VerifyScreen extends StatefulWidget {
const VerifyScreen({super.key});
@@ -9,6 +11,16 @@ class VerifyScreen extends StatefulWidget {
}
class _VerifyScreenState extends State<VerifyScreen> {
late VerifyController verifyController;
String? errorMessage;
@override
void initState() {
super.initState();
verifyController = VerifyController(context);
}
final List<TextEditingController> _codeControllers = List.generate(
6,
(index) => TextEditingController(),
@@ -26,6 +38,12 @@ class _VerifyScreenState extends State<VerifyScreen> {
super.dispose();
}
void showErrorMessage(String? message) {
setState(() {
errorMessage = message;
});
}
void _onCodeChanged(String value, int index) {
if (value.length == 1 && index < 5) {
_focusNodes[index + 1].requestFocus();
@@ -34,6 +52,9 @@ class _VerifyScreenState extends State<VerifyScreen> {
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: verifyController,
builder: (context, child) {
return Scaffold(
body: SingleChildScrollView(
child: Padding(
@@ -65,7 +86,7 @@ class _VerifyScreenState extends State<VerifyScreen> {
6,
(index) => SizedBox(
width: 50,
child: TextField(
child: TextFormField(
controller: _codeControllers[index],
focusNode: _focusNodes[index],
textAlign: TextAlign.center,
@@ -91,6 +112,9 @@ class _VerifyScreenState extends State<VerifyScreen> {
),
),
),
SizedBox(height: 5),
if(errorMessage != null)
Text(errorMessage ?? '', style: TextStyle(color: Colors.red)),
SizedBox(height: 30),
// Resend Code Section
Row(
@@ -102,7 +126,7 @@ class _VerifyScreenState extends State<VerifyScreen> {
),
TextButton(
onPressed: () {
// TODO: Add resend code logic
verifyController.resendOtp();
},
child: Text(
'Resend Code',
@@ -115,12 +139,12 @@ class _VerifyScreenState extends State<VerifyScreen> {
),
],
),
SizedBox(height: 20),
// Timer for resend (optional)
Text(
'Resend available in 2:30',
style: TextStyle(color: Colors.grey.shade500, fontSize: 14),
),
// SizedBox(height: 20),
// // Timer for resend (optional)
// Text(
// 'Resend available in 2:30',
// style: TextStyle(color: Colors.grey.shade500, fontSize: 14),
// ),
SizedBox(height: 40),
],
),
@@ -132,12 +156,28 @@ class _VerifyScreenState extends State<VerifyScreen> {
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Skeletonizer(
enabled: verifyController.model.isLoading,
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
context.go('/onboarding/password');
onPressed: () async {
showErrorMessage(null);
String code = '';
for (var controller in _codeControllers) {
code += controller.text;
}
if(code.length != 6) {
showErrorMessage('Invalid code');
return;
}
verifyController.updateCode(code);
await verifyController.verifyOtp();
if(verifyController.model.status == 'done') {
context.go('/onboarding/complete');
}
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 16),
@@ -173,6 +213,7 @@ class _VerifyScreenState extends State<VerifyScreen> {
],
),
),
),
SizedBox(height: 20),
TextButton(
onPressed: () {
@@ -185,4 +226,6 @@ class _VerifyScreenState extends State<VerifyScreen> {
),
);
}
);
}
}

View File

@@ -87,6 +87,7 @@ class ReceiptController extends ChangeNotifier {
creditPhone: model.receiptData?['creditPhone'] ?? '',
paymentProcessorLabel: model.receiptData?['paymentProcessorLabel'] ?? '',
paymentProcessorName: model.receiptData?['paymentProcessorName'] ?? '',
id: null
);
setLoading(false);

View File

@@ -15,7 +15,6 @@ class TransactionModel {
BillProvider? selectedProvider;
Map<String, dynamic>? receiptData;
FormData formData = FormData(
id: '',
type: '',
billClientId: '',
debitRef: '',
@@ -48,9 +47,9 @@ class TransactionModel {
@freezed
abstract class FormData with _$FormData {
const factory FormData({
String? id,
required String type, // CONFIRM, REQUEST, REVERSE
required String billClientId,
required String id,
required String debitRef,
required String debitCurrency,
required String amount,
@@ -117,7 +116,7 @@ class TransactionController extends ChangeNotifier {
clearFormData() {
model.formData = FormData(
id: '',
id: null,
type: '',
billClientId: '',
debitRef: '',

View File

@@ -16,8 +16,8 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$FormData {
String get type;// CONFIRM, REQUEST, REVERSE
String get billClientId; String get id; String get debitRef; String get debitCurrency; String get amount; String get creditAccount; String? get creditPhone; String? get creditName; String? get creditEmail; String get billName; String? get errorMessage; String get status; String get paymentProcessorLabel; String get paymentProcessorName; String get paymentProcessorImage; String get providerImage; String get providerLabel; String get userId; String? get debitPhone; String? get debitAccount; String? get productUid; String? get trace; String? get authType; String? get charge; String? get gatewayCharge; String? get tax; String? get totalAmount;
String? get id; String get type;// CONFIRM, REQUEST, REVERSE
String get billClientId; String get debitRef; String get debitCurrency; String get amount; String get creditAccount; String? get creditPhone; String? get creditName; String? get creditEmail; String get billName; String? get errorMessage; String get status; String get paymentProcessorLabel; String get paymentProcessorName; String get paymentProcessorImage; String get providerImage; String get providerLabel; String get userId; String? get debitPhone; String? get debitAccount; String? get productUid; String? get trace; String? get authType; String? get charge; String? get gatewayCharge; String? get tax; String? get totalAmount;
/// Create a copy of FormData
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -30,16 +30,16 @@ $FormDataCopyWith<FormData> get copyWith => _$FormDataCopyWithImpl<FormData>(thi
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is FormData&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.id, id) || other.id == id)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount));
return identical(this, other) || (other.runtimeType == runtimeType&&other is FormData&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hashAll([runtimeType,type,billClientId,id,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]);
int get hashCode => Object.hashAll([runtimeType,id,type,billClientId,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]);
@override
String toString() {
return 'FormData(type: $type, billClientId: $billClientId, id: $id, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)';
return 'FormData(id: $id, type: $type, billClientId: $billClientId, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)';
}
@@ -50,7 +50,7 @@ abstract mixin class $FormDataCopyWith<$Res> {
factory $FormDataCopyWith(FormData value, $Res Function(FormData) _then) = _$FormDataCopyWithImpl;
@useResult
$Res call({
String type, String billClientId, String id, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount
String? id, String type, String billClientId, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount
});
@@ -67,11 +67,11 @@ class _$FormDataCopyWithImpl<$Res>
/// Create a copy of FormData
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? billClientId = null,Object? id = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = freezed,Object? type = null,Object? billClientId = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) {
return _then(_self.copyWith(
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,billClientId: null == billClientId ? _self.billClientId : billClientId // ignore: cast_nullable_to_non_nullable
as String,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,debitRef: null == debitRef ? _self.debitRef : debitRef // ignore: cast_nullable_to_non_nullable
as String,debitCurrency: null == debitCurrency ? _self.debitCurrency : debitCurrency // ignore: cast_nullable_to_non_nullable
as String,amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable
@@ -108,13 +108,13 @@ as String?,
@JsonSerializable()
class _FormData implements FormData {
const _FormData({required this.type, required this.billClientId, required this.id, required this.debitRef, required this.debitCurrency, required this.amount, required this.creditAccount, required this.creditPhone, required this.creditName, required this.creditEmail, required this.billName, required this.errorMessage, required this.status, required this.paymentProcessorLabel, required this.paymentProcessorName, required this.paymentProcessorImage, required this.providerImage, required this.providerLabel, required this.userId, this.debitPhone, this.debitAccount, this.productUid, this.trace, this.authType, this.charge, this.gatewayCharge, this.tax, this.totalAmount});
const _FormData({this.id, required this.type, required this.billClientId, required this.debitRef, required this.debitCurrency, required this.amount, required this.creditAccount, required this.creditPhone, required this.creditName, required this.creditEmail, required this.billName, required this.errorMessage, required this.status, required this.paymentProcessorLabel, required this.paymentProcessorName, required this.paymentProcessorImage, required this.providerImage, required this.providerLabel, required this.userId, this.debitPhone, this.debitAccount, this.productUid, this.trace, this.authType, this.charge, this.gatewayCharge, this.tax, this.totalAmount});
factory _FormData.fromJson(Map<String, dynamic> json) => _$FormDataFromJson(json);
@override final String? id;
@override final String type;
// CONFIRM, REQUEST, REVERSE
@override final String billClientId;
@override final String id;
@override final String debitRef;
@override final String debitCurrency;
@override final String amount;
@@ -154,16 +154,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _FormData&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.id, id) || other.id == id)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _FormData&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.billClientId, billClientId) || other.billClientId == billClientId)&&(identical(other.debitRef, debitRef) || other.debitRef == debitRef)&&(identical(other.debitCurrency, debitCurrency) || other.debitCurrency == debitCurrency)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.creditAccount, creditAccount) || other.creditAccount == creditAccount)&&(identical(other.creditPhone, creditPhone) || other.creditPhone == creditPhone)&&(identical(other.creditName, creditName) || other.creditName == creditName)&&(identical(other.creditEmail, creditEmail) || other.creditEmail == creditEmail)&&(identical(other.billName, billName) || other.billName == billName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.status, status) || other.status == status)&&(identical(other.paymentProcessorLabel, paymentProcessorLabel) || other.paymentProcessorLabel == paymentProcessorLabel)&&(identical(other.paymentProcessorName, paymentProcessorName) || other.paymentProcessorName == paymentProcessorName)&&(identical(other.paymentProcessorImage, paymentProcessorImage) || other.paymentProcessorImage == paymentProcessorImage)&&(identical(other.providerImage, providerImage) || other.providerImage == providerImage)&&(identical(other.providerLabel, providerLabel) || other.providerLabel == providerLabel)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.debitPhone, debitPhone) || other.debitPhone == debitPhone)&&(identical(other.debitAccount, debitAccount) || other.debitAccount == debitAccount)&&(identical(other.productUid, productUid) || other.productUid == productUid)&&(identical(other.trace, trace) || other.trace == trace)&&(identical(other.authType, authType) || other.authType == authType)&&(identical(other.charge, charge) || other.charge == charge)&&(identical(other.gatewayCharge, gatewayCharge) || other.gatewayCharge == gatewayCharge)&&(identical(other.tax, tax) || other.tax == tax)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hashAll([runtimeType,type,billClientId,id,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]);
int get hashCode => Object.hashAll([runtimeType,id,type,billClientId,debitRef,debitCurrency,amount,creditAccount,creditPhone,creditName,creditEmail,billName,errorMessage,status,paymentProcessorLabel,paymentProcessorName,paymentProcessorImage,providerImage,providerLabel,userId,debitPhone,debitAccount,productUid,trace,authType,charge,gatewayCharge,tax,totalAmount]);
@override
String toString() {
return 'FormData(type: $type, billClientId: $billClientId, id: $id, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)';
return 'FormData(id: $id, type: $type, billClientId: $billClientId, debitRef: $debitRef, debitCurrency: $debitCurrency, amount: $amount, creditAccount: $creditAccount, creditPhone: $creditPhone, creditName: $creditName, creditEmail: $creditEmail, billName: $billName, errorMessage: $errorMessage, status: $status, paymentProcessorLabel: $paymentProcessorLabel, paymentProcessorName: $paymentProcessorName, paymentProcessorImage: $paymentProcessorImage, providerImage: $providerImage, providerLabel: $providerLabel, userId: $userId, debitPhone: $debitPhone, debitAccount: $debitAccount, productUid: $productUid, trace: $trace, authType: $authType, charge: $charge, gatewayCharge: $gatewayCharge, tax: $tax, totalAmount: $totalAmount)';
}
@@ -174,7 +174,7 @@ abstract mixin class _$FormDataCopyWith<$Res> implements $FormDataCopyWith<$Res>
factory _$FormDataCopyWith(_FormData value, $Res Function(_FormData) _then) = __$FormDataCopyWithImpl;
@override @useResult
$Res call({
String type, String billClientId, String id, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount
String? id, String type, String billClientId, String debitRef, String debitCurrency, String amount, String creditAccount, String? creditPhone, String? creditName, String? creditEmail, String billName, String? errorMessage, String status, String paymentProcessorLabel, String paymentProcessorName, String paymentProcessorImage, String providerImage, String providerLabel, String userId, String? debitPhone, String? debitAccount, String? productUid, String? trace, String? authType, String? charge, String? gatewayCharge, String? tax, String? totalAmount
});
@@ -191,11 +191,11 @@ class __$FormDataCopyWithImpl<$Res>
/// Create a copy of FormData
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? billClientId = null,Object? id = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = freezed,Object? type = null,Object? billClientId = null,Object? debitRef = null,Object? debitCurrency = null,Object? amount = null,Object? creditAccount = null,Object? creditPhone = freezed,Object? creditName = freezed,Object? creditEmail = freezed,Object? billName = null,Object? errorMessage = freezed,Object? status = null,Object? paymentProcessorLabel = null,Object? paymentProcessorName = null,Object? paymentProcessorImage = null,Object? providerImage = null,Object? providerLabel = null,Object? userId = null,Object? debitPhone = freezed,Object? debitAccount = freezed,Object? productUid = freezed,Object? trace = freezed,Object? authType = freezed,Object? charge = freezed,Object? gatewayCharge = freezed,Object? tax = freezed,Object? totalAmount = freezed,}) {
return _then(_FormData(
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,billClientId: null == billClientId ? _self.billClientId : billClientId // ignore: cast_nullable_to_non_nullable
as String,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,debitRef: null == debitRef ? _self.debitRef : debitRef // ignore: cast_nullable_to_non_nullable
as String,debitCurrency: null == debitCurrency ? _self.debitCurrency : debitCurrency // ignore: cast_nullable_to_non_nullable
as String,amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable

View File

@@ -7,9 +7,9 @@ part of 'transaction_controller.dart';
// **************************************************************************
_FormData _$FormDataFromJson(Map<String, dynamic> json) => _FormData(
id: json['id'] as String?,
type: json['type'] as String,
billClientId: json['billClientId'] as String,
id: json['id'] as String,
debitRef: json['debitRef'] as String,
debitCurrency: json['debitCurrency'] as String,
amount: json['amount'] as String,
@@ -38,9 +38,9 @@ _FormData _$FormDataFromJson(Map<String, dynamic> json) => _FormData(
);
Map<String, dynamic> _$FormDataToJson(_FormData instance) => <String, dynamic>{
'id': instance.id,
'type': instance.type,
'billClientId': instance.billClientId,
'id': instance.id,
'debitRef': instance.debitRef,
'debitCurrency': instance.debitCurrency,
'amount': instance.amount,

View File

@@ -38,7 +38,7 @@ class AppTheme {
const TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
),
),
cardTheme: CardTheme(
cardTheme: CardThemeData(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
),

View File

@@ -5,12 +5,16 @@
import FlutterMacOS
import Foundation
import firebase_core
import google_sign_in_ios
import path_provider_foundation
import share_plus
import shared_preferences_foundation
import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

View File

@@ -225,6 +225,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
sha256: "967dae9a65f69377beb9f4ab292ea63ce5befa1ce24682cab1b69ca4b7a46927"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: "5dbc900677dcbe5873d22ad7fbd64b047750124f1f9b7ebe2a33b9ddccc838eb"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: f7ee08febc1c4451588ce58ffcf28edaee857e9a196fee88b85deb889990094a
url: "https://pub.dev"
source: hosted
version: "3.1.0"
fixnum:
dependency: transitive
description:
@@ -336,6 +360,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.2.1"
google_identity_services_web:
dependency: transitive
description:
name: google_identity_services_web
sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454"
url: "https://pub.dev"
source: hosted
version: "0.3.3+1"
google_sign_in:
dependency: "direct main"
description:
name: google_sign_in
sha256: "939a8b58f84c4053811b8c1bc9adbcb59449a15b37958264bbf60020698cca0e"
url: "https://pub.dev"
source: hosted
version: "7.1.1"
google_sign_in_android:
dependency: transitive
description:
name: google_sign_in_android
sha256: d2c80c17ede83c2f3348847520bf78bc9ada3ff4f94361b0d7e93a3b9d7815a0
url: "https://pub.dev"
source: hosted
version: "7.1.0"
google_sign_in_ios:
dependency: transitive
description:
name: google_sign_in_ios
sha256: "10830fd37e6b4402b5c71373d3d8687e95ce7e9504738605237f23a76456c973"
url: "https://pub.dev"
source: hosted
version: "6.2.0"
google_sign_in_platform_interface:
dependency: transitive
description:
name: google_sign_in_platform_interface
sha256: "7f59208c42b415a3cca203571128d6f84f885fead2d5b53eb65a9e27f2965bb5"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
google_sign_in_web:
dependency: transitive
description:
name: google_sign_in_web
sha256: "2fc1f941e6443b2d6984f4056a727a3eaeab15d8ee99ba7125d79029be75a1da"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
graphs:
dependency: transitive
description:
@@ -1015,4 +1087,4 @@ packages:
version: "3.1.3"
sdks:
dart: ">=3.7.2 <4.0.0"
flutter: ">=3.27.0"
flutter: ">=3.29.0"

View File

@@ -53,6 +53,8 @@ dependencies:
share_plus: ^11.0.0
flutter_contacts: ^1.1.9+2
gif_view: ^0.4.0
google_sign_in: ^7.1.1
firebase_core: ^4.1.0
dev_dependencies:
flutter_test:

View File

@@ -6,10 +6,13 @@
#include "generated_plugin_registrant.h"
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <share_plus/share_plus_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FirebaseCorePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
SharePlusWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar(

View File

@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
firebase_core
share_plus
url_launcher_windows
)