The thirdfactor library provides Flutter widgets for initializing the ThirdFactor Verification process in a Flutter application. It includes components for onboarding, web view, and client configurations.
Add the following dependency to your pubspec.yaml file:
dependencies:
thirdfactor: ^<latest_version>Run the following command to install the package:
flutter pub getimport 'package:thirdfactor/thirdfactor.dart';In your iOS project, you need to add a description for camera usage in the Info.plist file. Open ios/Runner/Info.plist and add the following lines:
<key>NSCameraUsageDescription</key>
<string>Describe why your application requires access to device camera.</string>For Android, you'll need to add the camera permission to your AndroidManifest.xml file and provide a rationale for the user. Open android/app/src/main/AndroidManifest.xml and add the following lines:
<uses-permission android:name="android.permission.CAMERA" />Wrap your application with ThirdFactorScope to enable ThirdFactor Verification where clientId is optional :
void main() {
runApp(
ThirdFactorScope(
clientId: "YOUR_CLIENT_ID",
builder: (context, navigatorKey) {
return MaterialApp(
navigatorKey: navigatorKey,
// Your app configuration here
);
},
),
);
}You can customize the transition animation when pushing or popping routes using the transitionBuilder parameter in ThirdFactorScope.
ThirdFactorScope(
// Other parameters ....
transitionBuilder: (_, animation, __, child) {
const begin = Offset(1.0, 0.0);
const end = Offset.zero;
const curve = Curves.easeInOut;
var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
var offsetAnimation = animation.drive(tween);
return SlideTransition(
position: offsetAnimation,
child: child,
);
},
transitionDuration: Duration(millisecond:300),
reverseTransitionDuration: Duration(millisecond:300),
)You can customize the loading animation when initializing the ThirdFactor Verifcation.
ThirdFactorScope(
// Other parameters ....
loadingBuilder: (progress) {
return YourCustomLoadingWidget();
},
)Initiate the ThirdFactor Verification process in your app:
void startVerification() async {
// Get verification URL from your server
String verificationUrl = await yourServerApi.getVerificationUrl();
// Start ThirdFactor Verification
await ThirdFactorScope.of(context).startVerification(
verificationUrl: verificationUrl,
onCompletion: (response) {
// Handle the verification completion response
print("Verification Status: ${response.status}");
print("Verification Message: ${response.message}");
if (response.imageBytes != null) {
print("Image URL: ${response.imageBytes}");
}
// After the verification is completed, the customer receives a verification summary showing their document details, nationality, uploaded document images, and the final verification status.
setState(() {
tfResponse = response;
});
},
);
}
For more advanced configurations, you can customize the onboarding process using TfOnboardingOptions.
TfOnboardingOptions onboardingOptions = TfOnboardingOptions(
onboardingPages: [
// Your onboarding pages/widgets here
OnboardingPageWidget("Page 1", "Description 1", Icons.accessibility),
OnboardingPageWidget("Page 2", "Description 2", Icons.book),
OnboardingPageWidget("Page 3", "Description 3", Icons.camera),
],
onDone: () {
// Callback when Done button is pressed
print("Onboarding Completed");
},
onPageChanged: (index) {
// Callback when page changes
print("Page changed to $index");
},
showSkip: false,
showNext: true,
dotsDecorator: TfDotsDecorator(), // Dots decorator customization
animationDuration: 500,
curve: Curves.easeInOut,
controlsPadding: EdgeInsets.all(32.0),
scrollPhysics: BouncingScrollPhysics(),
controlAlignment: Alignment.topCenter, // Alignment for control buttons
);We use a JavaScript channel to handle messages from the Thirdfactor server. This is crucial for processing scanned documents or other data returned from the server.
tfResponse != null
? Card(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: tfResponse!.documentPhotos != null
? Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
tfResponse!.documentPhotos != null
? ListView.builder(
padding: const EdgeInsets.only(bottom: 8.0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: tfResponse!.documentPhotos!.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Image.memory(
base64Decode(
tfResponse!.documentPhotos![index].originalPhoto!,
),
height: size.height * 0.4,
),
);
},
)
: const SizedBox.shrink(),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("nationality: ${tfResponse!.documentPhotos![0].nationality}"),
Text("documentNumber: ${tfResponse!.documentPhotos![0].documentNumber}"),
],
)
],
),
)
: const SizedBox.shrink(),
)
: const SizedBox.shrink(),