JWT authentication for Flutter mobile app integrations.

flasho.auth

flasho.auth

JWT authentication is for mobile app integrations. For server-side or website integrations use an API key instead.

login()

Log in with dashboard email and password. The token is stored automatically.

final flasho = FlashoClient(token: 'placeholder');
 
final result = await flasho.auth.login(
  email: '[email protected]',
  password: 'your-password',
);
 
print(result.token);                   // eyJ...
print(result.user.email);              // [email protected]
print(result.merchant.sellerName);     // "My Restaurant"
print(result.merchant.billingMode);    // BillingMode.prepaid
print(result.merchant.walletBalance);  // "10.000"
 
// No setup needed — all subsequent calls use the JWT
final data = await flasho.account.bootstrap();

Response fields

FieldTypeDescription
tokenStringJWT for subsequent requests
user.idStringInternal user ID
user.emailStringAccount email
user.nameStringDisplay name
user.roleStringAlways "MERCHANT"
merchant.idStringInternal merchant ID
merchant.sellerIdStringMerchant code (e.g. M001)
merchant.sellerNameStringStore name
merchant.billingModeBillingModeprepaid or postpaid
merchant.walletBalanceStringCurrent balance in KWD
merchant.scheduledDeliveryEnabledboolWhether scheduled delivery is on

⚠️ Token expiry

JWTs expire. Handle FlashoApiException with isUnauthorized == true by re-calling login() and retrying the request.

Flutter login screen example

class LoginScreen extends StatefulWidget {
  const LoginScreen({super.key});
 
  @override
  State<LoginScreen> createState() => _LoginScreenState();
}
 
class _LoginScreenState extends State<LoginScreen> {
  final _emailController = TextEditingController();
  final _passwordController = TextEditingController();
  bool _loading = false;
  String? _error;
 
  Future<void> _login() async {
    setState(() { _loading = true; _error = null; });
 
    try {
      final flasho = context.read<FlashoClient>();
      final result = await flasho.auth.login(
        email: _emailController.text.trim(),
        password: _passwordController.text,
      );
      // Navigate to home with merchant info
      if (mounted) {
        Navigator.pushReplacementNamed(context, '/home',
          arguments: result.merchant);
      }
    } on FlashoApiException catch (e) {
      setState(() => _error = e.message);
    } on FlashoNetworkException catch (_) {
      setState(() => _error = 'Network error. Check your connection.');
    } finally {
      if (mounted) setState(() => _loading = false);
    }
  }
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: _emailController,
              decoration: const InputDecoration(labelText: 'Email'),
              keyboardType: TextInputType.emailAddress,
            ),
            const SizedBox(height: 16),
            TextField(
              controller: _passwordController,
              decoration: const InputDecoration(labelText: 'Password'),
              obscureText: true,
            ),
            const SizedBox(height: 24),
            if (_error != null) ...[
              Text(_error!, style: const TextStyle(color: Colors.red)),
              const SizedBox(height: 12),
            ],
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: _loading ? null : _login,
                child: _loading
                    ? const SizedBox(
                        height: 20,
                        width: 20,
                        child: CircularProgressIndicator(strokeWidth: 2),
                      )
                    : const Text('Sign in'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}