11 - How-To Guides

Cookbook-style recipes for common development tasks. Each recipe links to related pages for context.

How to Add a New Stage Screen

Suppose you need to add a new "Video KYC" stage between Stage 7 (Liveness) and Stage 8 (Signature). Here's the process.

  1. Create the screen file lib/screens/stage7b_video_kyc.dart
  2. Add a route constant in lib/config/routes.dart: static const stage7bVideoKyc = '/video-kyc';
  3. Register the route in lib/app.dart inside the routes map
  4. Navigate from Stage 7: replace pushReplacementNamed(stage8Signature) with pushReplacementNamed(stage7bVideoKyc)
  5. Navigate to Stage 8 at the end of your new screen's proceed handler
  6. Update JourneyProgressBar stage mapping if needed (for display name)
lib/screens/stage7b_video_kyc.dartDart
import 'package:flutter/material.dart';
import '../config/routes.dart';
import '../services/api_service.dart';
import '../services/storage_service.dart';
import '../widgets/back_button_handler.dart';
import '../widgets/session_timer.dart';
import '../widgets/loading_overlay.dart';
import '../widgets/progress_bar.dart';

class Stage7bVideoKycScreen extends StatefulWidget {
  const Stage7bVideoKycScreen({super.key});
  @override
  State<Stage7bVideoKycScreen> createState() => _Stage7bVideoKycState();
}

class _Stage7bVideoKycState extends State<Stage7bVideoKycScreen> {
  bool _isLoading = false;

  Future<void> _onProceed() async {
    setState(() => _isLoading = true);
    final storage = await StorageService.getInstance();
    final api = ApiService(storage: storage);
    final result = await api.submitVideoKyc(...);
    if (!mounted) return;
    setState(() => _isLoading = false);
    if (result.success) {
      Navigator.pushReplacementNamed(context, AppRoutes.stage8Signature);
    }
  }

  @override
  Widget build(BuildContext context) {
    return BackButtonHandler(
      child: SessionTimer(
        child: Scaffold(
          appBar: AppBar(title: const Text('Video KYC')),
          body: LoadingOverlay(
            isLoading: _isLoading,
            child: Column(
              children: [
                const JourneyProgressBar(currentStage: 7),
                const Expanded(child: Center(child: Text('Video KYC screen'))),
                ElevatedButton(onPressed: _onProceed, child: const Text('Proceed')),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

See 06 - Stage Walkthrough for the full anatomy of a stage screen.

How to Add a New API Method

Already covered in 07 - ApiService. Four steps:

  1. Add URL getter to ApiConfig
  2. Create a model with fromJson (if new shape)
  3. Add method to ApiService calling _post / _get
  4. Call from a screen

How to Add a New Domain Model

Models are plain Dart classes with a fromJson factory.

lib/models/video_kyc_result.dartDart
class VideoKycResult {
  final bool verified;
  final String? sessionId;
  final String? videoUrl;

  VideoKycResult({
    required this.verified,
    this.sessionId,
    this.videoUrl,
  });

  factory VideoKycResult.fromJson(Map<String, dynamic> json) {
    return VideoKycResult(
      verified: json['verified'] as bool? ?? false,
      sessionId: json['sessionId'] as String?,
      videoUrl: json['videoUrl'] as String?,
    );
  }
}

Keep models immutable (final fields, no setters). No methods beyond the factory and optional toJson.

How to Add a New Wrapper Widget

Wrapper widgets take a child and render something around it.

lib/widgets/debug_banner.dartDart
import 'package:flutter/material.dart';
import '../config/api_config.dart';

/// Shows a yellow "DEV BUILD" banner at the top of the screen in dev mode.
class DebugBanner extends StatelessWidget {
  final Widget child;
  const DebugBanner({super.key, required this.child});

  @override
  Widget build(BuildContext context) {
    if (!ApiConfig.isDev) return child;

    return Column(
      children: [
        Container(
          width: double.infinity,
          color: Colors.amber,
          padding: const EdgeInsets.symmetric(vertical: 4),
          child: const Text(
            'DEV BUILD - Mock Mode Active',
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold),
          ),
        ),
        Expanded(child: child),
      ],
    );
  }
}

Use it by wrapping your Scaffold body:

DartUsage
body: DebugBanner(
  child: Column(children: [...]),
)

How to Add a New Environment

Suppose you want to add a "staging" environment that points to a staging server.

  1. Edit lib/config/api_config.dart - add a case to the baseUrl getter
  2. Optional: add an isStaging getter
  3. Build with --dart-define=ENV=staging
lib/config/api_config.dartDart
static String get baseUrl {
  switch (_env) {
    case 'prod':    return 'https://ekyc.motilaloswal.com';
    case 'uat':     return 'https://ekycuat.motilaloswaluat.com';
    case 'staging': return 'https://ekycstaging.motilaloswaluat.com';
    default:       return 'http://localhost:5000';
  }
}

static bool get isStaging => _env == 'staging';

How to Add a New Asset (Lottie / SVG / PNG)

  1. Copy the file to flutter_app/assets/images/
  2. Add a constant to lib/config/assets.dart:
lib/config/assets.dartDart
class AppAssets {
  static const _base = 'assets/images';
  // Existing assets...
  static const videoKycIcon = '$_base/ic_video_kyc.svg';  // <-- add this
}
  1. Run flutter pub get (the assets/images/ folder is already registered, so new files are picked up automatically)
  2. Use it in a screen:
DartUsage
SvgPicture.asset(AppAssets.videoKycIcon, width: 64),
// or for Lottie:
Lottie.asset(AppAssets.myAnimation),
// or for PNG:
Image.asset(AppAssets.myIcon),

How to Add a Navigation Route

Routes are registered in lib/app.dart in the MaterialApp.routes map.

lib/app.dartDart
MaterialApp(
  routes: {
    AppRoutes.stage0Arrival:     (_) => const Stage0ArrivalScreen(),
    AppRoutes.stage1Registration:(_) => const Stage1RegistrationScreen(),
    AppRoutes.stage2Otp:         (_) => const Stage2OtpScreen(),
    // ... add your new route here
    AppRoutes.stage7bVideoKyc:   (_) => const Stage7bVideoKycScreen(),
  },
  initialRoute: AppRoutes.stage0Arrival,
)

How to Trigger a Build for a Specific Platform

See 09 - Config & Environments → Build Commands for the full command reference.

How to Run Tests

The project has a test/ folder but most tests are not yet written. Running the existing ones:

BashRun tests
cd flutter_app
C:\flutter\bin\flutter test
C:\flutter\bin\flutter test --coverage