qr_flow_sdk 0.2.1
qr_flow_sdk: ^0.2.1 copied to clipboard
Flutter SDK for the QR Flow API. Generate static and dynamic QR codes with a single API key. Includes local payload generators, a CRUD service for API-backed dynamic QR codes, and a customizable QrFlo [...]
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:qr_flow_sdk/qr_flow_sdk.dart';
void main() {
// Replace with your real API key.
QrFlow.init(apiKey: 'sk_test_development_key_001');
runApp(const QrFlowExampleApp());
}
class QrFlowExampleApp extends StatelessWidget {
const QrFlowExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'QR Flow SDK Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: Colors.indigo,
useMaterial3: true,
),
home: const _HomePage(),
);
}
}
class _HomePage extends StatelessWidget {
const _HomePage();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('QR Flow SDK')),
body: ListView(
children: [
ListTile(
leading: const Icon(Icons.qr_code),
title: const Text('Static QR Demo'),
trailing: const Icon(Icons.chevron_right),
onTap: () => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => const _StaticQrPage(),
),
),
),
ListTile(
leading: const Icon(Icons.dynamic_form),
title: const Text('Dynamic QR Demo'),
trailing: const Icon(Icons.chevron_right),
onTap: () => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => const _DynamicQrPage(),
),
),
),
],
),
);
}
}
// ─── Static QR ──────────────────────────────────────────────────────────────
class _StaticQrPage extends StatefulWidget {
const _StaticQrPage();
@override
State<_StaticQrPage> createState() => _StaticQrPageState();
}
class _StaticQrPageState extends State<_StaticQrPage> {
final _urlController = TextEditingController(text: 'https://example.com');
StaticQr? _qr;
@override
void dispose() {
_urlController.dispose();
super.dispose();
}
void _generate() {
setState(() {
_qr = QrFlow.staticQr.url(_urlController.text.trim());
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Static QR')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: _urlController,
decoration: const InputDecoration(
labelText: 'URL',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
FilledButton(
onPressed: _generate,
child: const Text('Generate'),
),
if (_qr != null) ...[
const SizedBox(height: 32),
Center(
child: QrFlowImage(
data: _qr!.payload,
size: 240,
style: const QrStyle(
foreground: '#1a1a2e',
shape: 'rounded',
),
),
),
const SizedBox(height: 16),
Text(
_qr!.payload,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall,
),
],
],
),
),
);
}
}
// ─── Dynamic QR ─────────────────────────────────────────────────────────────
class _DynamicQrPage extends StatefulWidget {
const _DynamicQrPage();
@override
State<_DynamicQrPage> createState() => _DynamicQrPageState();
}
class _DynamicQrPageState extends State<_DynamicQrPage> {
final _nameController = TextEditingController(text: 'My Menu');
final _urlController =
TextEditingController(text: 'https://restaurant.com/menu');
List<DynamicQr> _qrs = [];
bool _loading = false;
String? _error;
@override
void initState() {
super.initState();
_loadList();
}
@override
void dispose() {
_nameController.dispose();
_urlController.dispose();
super.dispose();
}
Future<void> _loadList() async {
setState(() {
_loading = true;
_error = null;
});
try {
final list = await QrFlow.dynamicQr.list();
setState(() => _qrs = list);
} on QrFlowException catch (e) {
setState(() => _error = e.message);
} finally {
setState(() => _loading = false);
}
}
Future<void> _create() async {
setState(() {
_loading = true;
_error = null;
});
try {
await QrFlow.dynamicQr.create(
name: _nameController.text.trim(),
targetUrl: _urlController.text.trim(),
type: 'menu',
);
await _loadList();
} on QrFlowException catch (e) {
setState(() => _error = e.message);
} finally {
setState(() => _loading = false);
}
}
Future<void> _toggle(DynamicQr qr) async {
try {
await QrFlow.dynamicQr.toggle(qr.id, isActive: !qr.isActive);
await _loadList();
} on QrFlowException catch (e) {
setState(() => _error = e.message);
}
}
Future<void> _delete(DynamicQr qr) async {
try {
await QrFlow.dynamicQr.delete(qr.id);
await _loadList();
} on QrFlowException catch (e) {
setState(() => _error = e.message);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dynamic QR'),
actions: [
IconButton(icon: const Icon(Icons.refresh), onPressed: _loadList),
],
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
// ── Create form ──────────────────────────────────────────
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Create Dynamic QR',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Name',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
TextField(
controller: _urlController,
decoration: const InputDecoration(
labelText: 'Target URL',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
FilledButton(
onPressed: _loading ? null : _create,
child: const Text('Create'),
),
],
),
),
),
if (_error != null) ...[
const SizedBox(height: 12),
Card(
color: Theme.of(context).colorScheme.errorContainer,
child: Padding(
padding: const EdgeInsets.all(12),
child: Text(
_error!,
style: TextStyle(
color: Theme.of(context).colorScheme.onErrorContainer,
),
),
),
),
],
const SizedBox(height: 16),
// ── List ─────────────────────────────────────────────────
if (_loading && _qrs.isEmpty)
const Center(child: CircularProgressIndicator())
else
..._qrs.map(
(qr) => _DynamicQrTile(
qr: qr,
onToggle: () => _toggle(qr),
onDelete: () => _delete(qr),
),
),
],
),
);
}
}
class _DynamicQrTile extends StatelessWidget {
const _DynamicQrTile({
required this.qr,
required this.onToggle,
required this.onDelete,
});
final DynamicQr qr;
final VoidCallback onToggle;
final VoidCallback onDelete;
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: Column(
children: [
ListTile(
leading: QrFlowImage.dynamic(qr: qr, size: 56, padding: 4),
title: Text(qr.name),
subtitle: Text(
qr.redirectUrl,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: Switch(
value: qr.isActive,
onChanged: (_) => onToggle(),
),
),
OverflowBar(
children: [
TextButton.icon(
icon: const Icon(Icons.delete_outline),
label: const Text('Delete'),
onPressed: onDelete,
),
],
),
],
),
);
}
}