strapi_ldk 2.0.3
strapi_ldk: ^2.0.3 copied to clipboard
A Flutter SDK (Lazy Development Kit / LDK) that wraps Strapi's REST & GraphQL APIs into a simple, type-safe, and intuitive client-side library.
import 'package:flutter/material.dart';
import 'package:strapi_ldk/strapi_ldk.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize LDK with your Strapi server URL
await LDK.initialize(
baseUrl: 'https://your-strapi-server.com', // Replace with your Strapi URL
enableLogging: true,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'LDK Example',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: const AuthWrapper(),
);
}
}
class AuthWrapper extends StatelessWidget {
const AuthWrapper({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<bool>(
stream: LDK.authService.authStateStream,
builder: (context, snapshot) {
final isAuthenticated = snapshot.data ?? false;
if (isAuthenticated) {
return const HomePage();
} else {
return const LoginPage();
}
},
);
}
}
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _usernameController = TextEditingController();
bool _isLoading = false;
bool _isSignUp = false;
String? _errorMessage;
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
_usernameController.dispose();
super.dispose();
}
Future<void> _signIn() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
await LDK.authService.signIn(
email: _emailController.text.trim(),
password: _passwordController.text,
);
} catch (e) {
setState(() {
_errorMessage = e.toString();
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _signUp() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
await LDK.authService.signUp(
email: _emailController.text.trim(),
password: _passwordController.text,
username: _usernameController.text.trim(),
);
} catch (e) {
setState(() {
_errorMessage = e.toString();
});
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isSignUp ? 'Sign Up' : 'Sign In'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 16),
if (_isSignUp) ...[
TextField(
controller: _usernameController,
decoration: const InputDecoration(
labelText: 'Username',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
],
TextField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
obscureText: true,
),
const SizedBox(height: 24),
if (_errorMessage != null) ...[
Text(
_errorMessage!,
style: const TextStyle(color: Colors.red),
),
const SizedBox(height: 16),
],
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _isLoading ? null : (_isSignUp ? _signUp : _signIn),
child: _isLoading
? const CircularProgressIndicator()
: Text(_isSignUp ? 'Sign Up' : 'Sign In'),
),
),
const SizedBox(height: 16),
TextButton(
onPressed: () {
setState(() {
_isSignUp = !_isSignUp;
_errorMessage = null;
});
},
child: Text(
_isSignUp
? 'Already have an account? Sign In'
: 'Don\'t have an account? Sign Up',
),
),
],
),
),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<Map<String, dynamic>> _posts = [];
bool _isLoading = false;
String? _errorMessage;
@override
void initState() {
super.initState();
_loadPosts();
}
Future<void> _loadPosts() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final postsCollection = LDK.collection('posts');
final query = postsCollection
.where('status', equals: 'published')
.sort('createdAt', descending: true)
.populate('author')
.limit(20);
final response = await postsCollection.get(query: query);
setState(() {
_posts = response.data;
});
} catch (e) {
setState(() {
_errorMessage = e.toString();
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _createPost() async {
final titleController = TextEditingController();
final contentController = TextEditingController();
final result = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Create New Post'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: titleController,
decoration: const InputDecoration(
labelText: 'Title',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: contentController,
decoration: const InputDecoration(
labelText: 'Content',
border: OutlineInputBorder(),
),
maxLines: 3,
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () async {
try {
await LDK.collection('posts').create({
'title': titleController.text,
'content': contentController.text,
'status': 'published',
});
if (context.mounted) {
Navigator.of(context).pop(true);
}
} catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
}
},
child: const Text('Create'),
),
],
),
);
if (result == true) {
_loadPosts();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Posts'),
actions: [
StreamBuilder<LDKUser?>(
stream: LDK.authService.userStream,
builder: (context, snapshot) {
final user = snapshot.data;
return PopupMenuButton(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.person),
const SizedBox(width: 4),
Text(user?.username ?? user?.email ?? 'User'),
],
),
),
itemBuilder: (context) => [
PopupMenuItem<void>(
onTap: () {
LDK.authService.signOut();
},
child: const Text('Sign Out'),
),
],
);
},
),
],
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: _errorMessage != null
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Error: $_errorMessage',
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadPosts,
child: const Text('Retry'),
),
],
),
)
: RefreshIndicator(
onRefresh: _loadPosts,
child: ListView.builder(
itemCount: _posts.length,
itemBuilder: (context, index) {
final post = _posts[index];
return Card(
margin: const EdgeInsets.all(8.0),
child: ListTile(
title: Text(post['title']?.toString() ?? 'Untitled'),
subtitle: Text(
post['content']?.toString() ?? 'No content',
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
trailing: Text(
'By ${post['author']?['username']?.toString() ?? 'Unknown'}',
style: Theme.of(context).textTheme.bodySmall,
),
),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: _createPost,
child: const Icon(Icons.add),
),
);
}
}