kooza_flutter 1.0.3
kooza_flutter: ^1.0.3 copied to clipboard
A blazingly fast, reactive, fully asynchronous and easy to use local database for flutter
example/lib/main.dart
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kooza_flutter/kooza_flutter.dart';
import 'package:provider/provider.dart';
class Product {
final String? id;
final String? name;
final double? price;
const Product({this.id, this.name, this.price});
Product copyWith({
String? id,
String? name,
double? price,
}) {
return Product(
id: id ?? this.id,
name: name ?? this.name,
price: price ?? this.price,
);
}
factory Product.fromMap(Map<String, dynamic>? map, [String? id]) {
if (map == null) return const Product();
return Product(
id: id,
name: map['name'],
price: (map['price'] as num?)?.toDouble(),
);
}
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
'id': id,
'name': name,
'price': price,
};
map.removeWhere((key, value) => value == null);
return map;
}
}
class ProductsState {
final bool isDarkMode;
final Product product;
final List<Product> products;
const ProductsState({
this.isDarkMode = false,
this.product = const Product(),
this.products = const [],
});
ProductsState copyWith({
bool? isDarkMode,
Product? product,
List<Product>? products,
}) {
return ProductsState(
isDarkMode: isDarkMode ?? this.isDarkMode,
product: product ?? this.product,
products: products ?? this.products,
);
}
}
class ProductsBloc extends Cubit<ProductsState> {
final Kooza _kooza;
ProductsBloc(Kooza kooza)
: _kooza = kooza,
super(const ProductsState()) {
streamDarkMode();
streamProducts();
}
void setProductId(String? id) {
emit(state.copyWith(product: state.product.copyWith(id: id)));
}
void setProductName(String? name) {
emit(state.copyWith(product: state.product.copyWith(name: name)));
}
void setProductPrice(String? price) {
final newPrice = double.tryParse(price ?? '0.0');
emit(state.copyWith(product: state.product.copyWith(price: newPrice)));
}
StreamSubscription<List<Product>>? _producstsSub;
void streamProducts() {
final ref = _kooza.collection('products').snapshots();
final productsStream = ref.map((collection) => collection.docs.values
.map((doc) => Product.fromMap(doc.data, doc.id))
.toList());
_producstsSub?.cancel();
_producstsSub = productsStream.listen((products) {
emit(state.copyWith(products: products));
// ignore: avoid_print
}, onError: (e) => kDebugMode ? print('Error: $e') : null);
}
void fetchProducts() async {
try {
await _producstsSub?.cancel();
final ref = await _kooza.collection('products').get();
final result = ref.docs.values
.map((doc) => Product.fromMap(doc.data, doc.id))
.toList();
emit(state.copyWith(products: result));
} catch (e) {
if (kDebugMode) print('Error fetching products: $e');
}
}
void saveProduct() async {
try {
final id = await _kooza.collection('products').add(state.product.toMap());
emit(state.copyWith(product: state.product.copyWith(id: id)));
} catch (e) {
if (kDebugMode) print('Error saving products: $e');
}
}
void deleteProduct(String? id) async {
try {
if (id == null) return;
await _kooza.collection('products').deleteDoc(id);
} catch (e) {
if (kDebugMode) print('Error saving products: $e');
}
}
void deleteAll() async {
try {
// await _kooza.collection('products').delete();
await _kooza.clear();
await _kooza.clearAllInstances();
} catch (e) {
if (kDebugMode) print('Error saving products: $e');
}
}
StreamSubscription? _darkmodeSub;
void streamDarkMode() {
_darkmodeSub?.cancel();
_darkmodeSub = _kooza.singleDoc('isDarkMode').snapshots().listen(
(event) => emit(state.copyWith(isDarkMode: event.data as bool?)));
}
void setDarkMode(bool value) async {
try {
await _kooza.singleDoc('isDarkMode').set(value);
} catch (e) {
if (kDebugMode) print(e);
}
}
@override
Future<void> close() async {
await _producstsSub?.cancel();
await _kooza.close();
return super.close();
}
}
void main() async {
await Kooza.ensureInitialize();
runApp(const AppDataProvider());
}
class AppDataProvider extends StatelessWidget {
const AppDataProvider({super.key});
@override
Widget build(BuildContext context) {
return Provider(
create: (context) => Kooza.instance('products'),
dispose: (_, kooza) async => await kooza.close(),
child: BlocProvider(
create: (c) => ProductsBloc(c.read<Kooza>()),
child: const AppGeneralSetup(),
),
);
}
}
class AppGeneralSetup extends StatelessWidget {
const AppGeneralSetup({super.key});
@override
Widget build(BuildContext context) {
return BlocSelector<ProductsBloc, ProductsState, bool>(
selector: (state) => state.isDarkMode,
builder: (context, isDarkMode) => MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Kooza Example App',
theme: isDarkMode ? ThemeData.dark() : ThemeData.light(),
home: const KoozaHomePage(),
),
);
}
}
class FormCreateProduct extends StatefulWidget {
const FormCreateProduct({super.key});
@override
State<FormCreateProduct> createState() => _FormCreateProductState();
}
class _FormCreateProductState extends State<FormCreateProduct> {
final _formKey = GlobalKey<FormState>();
void _saveProduct(BuildContext context) {
final formState = _formKey.currentState;
if (formState?.validate() == true) {
formState?.save();
context.read<ProductsBloc>().saveProduct();
formState?.reset();
}
}
@override
Widget build(BuildContext context) {
final idField = BlocSelector<ProductsBloc, ProductsState, String?>(
selector: (state) => state.product.id,
builder: (context, id) => TextFormField(
decoration: const InputDecoration(hintText: 'Product ID'),
initialValue: id,
onSaved: context.read<ProductsBloc>().setProductId,
),
);
final nameField = BlocSelector<ProductsBloc, ProductsState, String?>(
selector: (state) => state.product.name,
builder: (context, name) => TextFormField(
decoration: const InputDecoration(hintText: 'Product Name'),
initialValue: name,
validator: (v) => (v?.trim().isEmpty ?? true) ? 'Add Name' : null,
onSaved: context.read<ProductsBloc>().setProductName,
),
);
final priceField = BlocSelector<ProductsBloc, ProductsState, double?>(
selector: (state) => state.product.price,
builder: (context, price) => TextFormField(
decoration: const InputDecoration(hintText: 'Product Price'),
initialValue: price?.toString(),
onSaved: context.read<ProductsBloc>().setProductPrice,
),
);
final saveBtn = TextButton(
onPressed: () => _saveProduct(context),
child: const Padding(padding: EdgeInsets.all(8.0), child: Text("Save")),
);
final fields = Padding(
padding: const EdgeInsets.all(40.0),
child: Column(children: [idField, nameField, priceField, saveBtn]),
);
return Form(key: _formKey, child: fields);
}
}
class ListProducts extends StatelessWidget {
const ListProducts({super.key});
@override
Widget build(BuildContext context) {
return BlocSelector<ProductsBloc, ProductsState, List<Product>>(
selector: (state) => state.products,
builder: (context, products) => ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) => ListTile(
leading: IconButton(
onPressed: () =>
context.read<ProductsBloc>().deleteProduct(products[index].id),
icon: const Icon(Icons.delete),
),
trailing: Text(products[index].price?.toString() ?? '0.0'),
title: Text(products[index].name ?? ''),
subtitle: Text(products[index].id ?? ''),
),
),
);
}
}
class KoozaHomePage extends StatelessWidget {
const KoozaHomePage({super.key});
@override
Widget build(BuildContext context) {
final darkModeBtn = BlocSelector<ProductsBloc, ProductsState, bool>(
selector: (state) => state.isDarkMode,
builder: (context, state) => Switch(
onChanged: (v) => context.read<ProductsBloc>().setDarkMode(v),
value: state,
),
);
final deleteAllBtn = IconButton(
onPressed: () => context.read<ProductsBloc>().deleteAll(),
icon: const Icon(Icons.delete),
);
return Scaffold(
appBar: AppBar(
title: const Text('Kooza Example App'),
actions: [darkModeBtn, deleteAllBtn],
),
body: Column(
children: const [
FormCreateProduct(),
Expanded(child: ListProducts()),
],
),
);
}
}
class ProductInputField extends StatelessWidget {
final void Function(String? value) onSaved;
final String hint;
const ProductInputField({
super.key,
required this.onSaved,
required this.hint,
});
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(hintText: hint),
onSaved: onSaved,
);
}
}