The Dynamic Flutter SDK supports SMS-based authentication using OTP (One-Time Password) verification. Users can sign in by receiving a verification code sent to their phone number.

SMS Authentication Flow

SMS authentication must be enabled in your environment’s dashboard settings and requires a configured SMS provider.

Send SMS OTP

Send a verification code to the user’s phone number using the PhoneData structure:
OutlinedButton(
  onPressed: () async {
    await DynamicSDK.instance.auth.sms.sendOTP(
      PhoneData(dialCode: "+1", iso2: "US", phone: "1234567890")
    );
  },
  child: const Text('Send SMS Code'),
)

Verify SMS OTP

Verify the OTP code entered by the user:
OutlinedButton(
  onPressed: () async {
    await DynamicSDK.instance.auth.sms.verifyOTP("123456");
  },
  child: const Text('Verify SMS Code'),
)

Resend SMS OTP

Allow users to request a new OTP code if the previous one expired or wasn’t received:
OutlinedButton(
  onPressed: () async {
    await DynamicSDK.instance.auth.sms.resendOTP();
  },
  child: const Text('Resend SMS Code'),
)

PhoneData Structure

The SMS authentication methods use a PhoneData object that contains:
  • dialCode: The country dial code (e.g., “+1” for US, “+44” for UK)
  • iso2: The two-letter country code (e.g., “US”, “UK”, “JP”)
  • phone: The phone number without the country code

Examples

// US phone number
PhoneData(dialCode: "+1", iso2: "US", phone: "1234567890")

// UK phone number  
PhoneData(dialCode: "+44", iso2: "GB", phone: "1234567890")

// Japan phone number
PhoneData(dialCode: "+81", iso2: "JP", phone: "1234567890")

Configuration

Dashboard Settings

Configure SMS authentication in your Dynamic dashboard:
  1. Enable SMS Authentication:
  2. Phone Number Settings (Optional):
    • You can provide your own SMS provider credentials in the phone number settings
    • This is required for phone numbers outside of US and Canada

Complete Authentication Flow

Here’s a complete example of the SMS authentication flow:
class SmsAuthScreen extends StatefulWidget {
  @override
  _SmsAuthScreenState createState() => _SmsAuthScreenState();
}

class _SmsAuthScreenState extends State<SmsAuthScreen> {
  final TextEditingController _phoneController = TextEditingController();
  final TextEditingController _otpController = TextEditingController();
  String _selectedDialCode = '+1';
  String _selectedCountryCode = 'US';
  bool _otpSent = false;

  Future<void> _sendOTP() async {
    try {
      final phoneData = PhoneData(
        dialCode: _selectedDialCode,
        iso2: _selectedCountryCode,
        phone: _phoneController.text,
      );
      await DynamicSDK.instance.auth.sms.sendOTP(phoneData);
      setState(() {
        _otpSent = true;
      });
    } catch (e) {
      // Handle error
      print('Error sending SMS OTP: $e');
    }
  }

  Future<void> _verifyOTP() async {
    try {
      await DynamicSDK.instance.auth.sms.verifyOTP(_otpController.text);
      // User is now authenticated
      Navigator.pushReplacementNamed(context, '/home');
    } catch (e) {
      // Handle error
      print('Error verifying SMS OTP: $e');
    }
  }

  Future<void> _resendOTP() async {
    try {
      await DynamicSDK.instance.auth.sms.resendOTP();
      // Show success message
    } catch (e) {
      // Handle error
      print('Error resending SMS OTP: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('SMS Authentication')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            if (!_otpSent) ...[
              Row(
                children: [
                  DropdownButton<String>(
                    value: _selectedDialCode,
                    items: [
                      DropdownMenuItem(value: '+1', child: Text('+1 (US)')),
                      DropdownMenuItem(value: '+44', child: Text('+44 (UK)')),
                      DropdownMenuItem(value: '+81', child: Text('+81 (JP)')),
                    ],
                    onChanged: (value) {
                      setState(() {
                        _selectedDialCode = value!;
                        _selectedCountryCode = value == '+1' ? 'US' : 
                                             value == '+44' ? 'GB' : 'JP';
                      });
                    },
                  ),
                  SizedBox(width: 8),
                  Expanded(
                    child: TextField(
                      controller: _phoneController,
                      decoration: InputDecoration(
                        labelText: 'Phone Number',
                        border: OutlineInputBorder(),
                      ),
                      keyboardType: TextInputType.phone,
                    ),
                  ),
                ],
              ),
              SizedBox(height: 16),
              ElevatedButton(
                onPressed: _sendOTP,
                child: Text('Send SMS Code'),
              ),
            ] else ...[
              Text('Enter the verification code sent to $_selectedDialCode${_phoneController.text}'),
              SizedBox(height: 16),
              TextField(
                controller: _otpController,
                decoration: InputDecoration(
                  labelText: 'Verification Code',
                  border: OutlineInputBorder(),
                ),
                keyboardType: TextInputType.number,
                maxLength: 6,
              ),
              SizedBox(height: 16),
              ElevatedButton(
                onPressed: _verifyOTP,
                child: Text('Verify Code'),
              ),
              SizedBox(height: 8),
              TextButton(
                onPressed: _resendOTP,
                child: Text('Resend Code'),
              ),
            ],
          ],
        ),
      ),
    );
  }
}
You can read more about the SMS authentication module here.