ironing out erp bugs
@@ -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
|
||||
|
||||
29
android/app/google-services.json
Normal 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"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 19 KiB |
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
After Width: | Height: | Size: 25 KiB |
BIN
assets/phone.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
assets/phone.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
assets/social.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
assets/social.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
1
firebase.json
Normal 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
17
lib/abstracts/AbstractController.dart
Normal 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
@@ -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',
|
||||
);
|
||||
|
||||
}
|
||||
@@ -17,7 +17,7 @@ class Http {
|
||||
LogInterceptor(
|
||||
request: true,
|
||||
responseBody: false,
|
||||
requestBody: false,
|
||||
requestBody: true,
|
||||
error: true,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
73
lib/screens/onboarding/complete_screen.dart
Normal 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),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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: () {
|
||||
|
||||
37
lib/screens/onboarding/onboarding_controller.dart
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
73
lib/screens/onboarding/phone/phone_controller.dart
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
270
lib/screens/onboarding/phone/phone_screen.dart
Normal 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;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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> {
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
95
lib/screens/onboarding/verify/verify_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ class ReceiptController extends ChangeNotifier {
|
||||
creditPhone: model.receiptData?['creditPhone'] ?? '',
|
||||
paymentProcessorLabel: model.receiptData?['paymentProcessorLabel'] ?? '',
|
||||
paymentProcessorName: model.receiptData?['paymentProcessorName'] ?? '',
|
||||
id: null
|
||||
);
|
||||
|
||||
setLoading(false);
|
||||
|
||||
@@ -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: '',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)),
|
||||
),
|
||||
|
||||
@@ -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"))
|
||||
|
||||
74
pubspec.lock
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
firebase_core
|
||||
share_plus
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||