uplift_funnel_flutter 0.8.0
uplift_funnel_flutter: ^0.8.0 copied to clipboard
Native onboarding and paywall flows for iOS, authored in a dashboard and updated without an app release. Rendered by the native engine, so a Flutter app and a native app draw a flow identically. A/B e [...]
example/lib/main.dart
// Demo host for the Uplift Funnel SDK.
//
// Nothing is baked in at build time: type a server URL, a public key
// (`fnl_pk_…` from the dashboard) and a flow key, then hit Start. One build
// points at any flow. The fields persist, so a hot restart keeps them.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uplift_funnel_flutter/uplift_funnel_flutter.dart';
void main() => runApp(const UpliftFunnelExampleApp());
class UpliftFunnelExampleApp extends StatelessWidget {
const UpliftFunnelExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Uplift Funnel demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF2E7D32)),
useMaterial3: true,
),
home: const DemoHome(),
);
}
}
class DemoHome extends StatefulWidget {
const DemoHome({super.key});
@override
State<DemoHome> createState() => _DemoHomeState();
}
class _DemoHomeState extends State<DemoHome> {
final _server = TextEditingController();
final _apiKey = TextEditingController();
final _flowKey = TextEditingController();
String? _status;
@override
void initState() {
super.initState();
SharedPreferences.getInstance().then((prefs) {
setState(() {
_server.text = prefs.getString('server') ?? 'http://localhost:3000';
_apiKey.text = prefs.getString('apiKey') ?? '';
_flowKey.text = prefs.getString('flowKey') ?? '';
});
});
}
Future<void> _start() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('server', _server.text);
await prefs.setString('apiKey', _apiKey.text);
await prefs.setString('flowKey', _flowKey.text);
try {
await UpliftFunnel.configure(
apiKey: _apiKey.text.trim(),
serverUrl: _server.text.trim().isEmpty ? null : _server.text.trim(),
);
} catch (error) {
setState(() => _status = '$error');
return;
}
if (!mounted) return;
await Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => Scaffold(
body: UpliftFunnelFlow(
_flowKey.text.trim(),
onCompleted: (result) {
setState(() => _status =
'ended: ${result.endReason} · ${result.variables.length} answers');
Navigator.of(context).maybePop();
},
),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Uplift Funnel demo')),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (!UpliftFunnel.isSupported)
const Padding(
padding: EdgeInsets.only(bottom: 16),
child: Text(
'The renderer is a native iOS engine — run this on an iPhone '
'or the simulator.',
),
),
TextField(
controller: _server,
decoration: const InputDecoration(labelText: 'Server URL'),
),
TextField(
controller: _apiKey,
decoration: const InputDecoration(labelText: 'Public key'),
),
TextField(
controller: _flowKey,
decoration: const InputDecoration(labelText: 'Flow key'),
),
const SizedBox(height: 20),
FilledButton(onPressed: _start, child: const Text('Start')),
if (_status != null) ...[
const SizedBox(height: 20),
Text(_status!, style: Theme.of(context).textTheme.bodySmall),
],
],
),
),
);
}
}