Skip to content

Instantly share code, notes, and snippets.

@solanoize
Last active July 30, 2024 18:01
Show Gist options
  • Select an option

  • Save solanoize/67cfc6be5d6caca1b4d8395aed914722 to your computer and use it in GitHub Desktop.

Select an option

Save solanoize/67cfc6be5d6caca1b4d8395aed914722 to your computer and use it in GitHub Desktop.
CRUD Flutter REST API
// lib/screens/product_create_screen.dart
import 'package:crud_flutter_rest/models/product.dart';
import 'package:crud_flutter_rest/services/product_service.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ProductCreateScreen extends StatefulWidget {
const ProductCreateScreen({super.key});
@override
State<ProductCreateScreen> createState() => _ProductCreateScreenState();
}
class _ProductCreateScreenState extends State<ProductCreateScreen> {
TextEditingController titleController = TextEditingController();
TextEditingController imageController = TextEditingController(
text: "https://loremflickr.com"
"/640/480/animals",
);
TextEditingController descriptionController = TextEditingController(
text: "Andy shoes are designed to keeping "
"in mind durability as well as trends",
);
TextEditingController priceController = TextEditingController(text: "34.90");
Future<Product>? futureProduct;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('New Product'),
),
body: (futureProduct == null) ? buildLayout() : buildFutureBuilder(),
);
}
SingleChildScrollView buildLayout() {
return SingleChildScrollView(
child: Column(
children: <Widget>[
SizedBox(height: 32),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: _title(),
),
SizedBox(height: 32),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: _image(),
),
SizedBox(height: 32),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: _description(),
),
SizedBox(height: 32),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: _price(),
),
SizedBox(height: 32),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: _save(),
),
SizedBox(height: 32),
],
),
);
}
TextField _title() {
OutlineInputBorder outlineInputBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
);
InputDecoration decoration = InputDecoration(
helperText: "Please input your product title",
labelText: "Title",
border: outlineInputBorder,
);
return TextField(
controller: titleController,
decoration: decoration,
);
}
TextField _image() {
OutlineInputBorder outlineInputBorder =
OutlineInputBorder(borderRadius: BorderRadius.circular(10));
InputDecoration decoration = InputDecoration(
helperText: "Please input image URL like https://...",
labelText: "Image (url)",
border: outlineInputBorder);
return TextField(
keyboardType: TextInputType.url,
controller: imageController,
decoration: decoration,
);
}
TextField _description() {
OutlineInputBorder outlineInputBorder =
OutlineInputBorder(borderRadius: BorderRadius.circular(10));
InputDecoration decoration = InputDecoration(
alignLabelWithHint: true,
labelText: "Description",
helperText: "Please input your product description",
border: outlineInputBorder);
return TextField(
maxLines: 4,
controller: descriptionController,
decoration: decoration,
);
}
TextField _price() {
OutlineInputBorder outlineInputBorder =
OutlineInputBorder(borderRadius: BorderRadius.circular(10));
InputDecoration decoration = InputDecoration(
helperText: "Please input your product price",
labelText: "Price",
border: outlineInputBorder);
return TextField(
keyboardType: TextInputType.number,
controller: priceController,
decoration: decoration,
);
}
TextButton _save() {
return TextButton(
onPressed: () async {
setState(() {
futureProduct = ProductService.create(
titleController.text,
imageController.text,
priceController.text,
descriptionController.text,
);
futureProduct?.then((Product product) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Success creating product"),
));
});
});
},
style: TextButton.styleFrom(
backgroundColor: Colors.blue,
minimumSize: Size(double.infinity, 30),
padding: EdgeInsets.symmetric(vertical: 16),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
),
),
child: Text(
"Save",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
);
}
FutureBuilder<Product> buildFutureBuilder() {
return FutureBuilder<Product>(
future: futureProduct,
builder: (BuildContext context, AsyncSnapshot<Product> snapshot) {
if (snapshot.connectionState == ConnectionState.none) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
}
return buildLayout();
}
return Center(
child: CircularProgressIndicator(),
);
},
);
}
}
// lib/screens/product_list_screen.dart
import 'package:crud_flutter_rest/models/product.dart';
import 'package:crud_flutter_rest/screens/product_create_screen.dart';
import 'package:crud_flutter_rest/screens/product_update_screen.dart';
import 'package:crud_flutter_rest/services/product_service.dart';
import 'package:flutter/material.dart';
class ProductListScreen extends StatefulWidget {
const ProductListScreen({super.key});
@override
State<ProductListScreen> createState() => _ProductListScreenState();
}
class _ProductListScreenState extends State<ProductListScreen> {
late Future<List<Product>> futureProducts;
TextEditingController searchController = TextEditingController();
@override
void initState() {
super.initState();
futureProducts = ProductService.fetchAll();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Products'),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blue,
tooltip: 'New',
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ProductCreateScreen();
}));
},
child: const Icon(Icons.add, color: Colors.white, size: 28),
),
body: buildFutureBuilder(),
);
}
FutureBuilder<List<Product>> buildFutureBuilder() {
return FutureBuilder<List<Product>>(
future: futureProducts,
builder: (BuildContext context, AsyncSnapshot<List<Product>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
List<Product> products = snapshot.data!;
return buildLayout(products: products);
} else if (snapshot.hasError) {
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(snapshot.error.toString()),
ElevatedButton(onPressed: onRefresh, child: Text("Refresh"))
],
),
);
}
}
return Center(child: CircularProgressIndicator());
},
);
}
RefreshIndicator buildLayout({required List<Product> products}) {
return RefreshIndicator(
onRefresh: onRefresh,
child: SafeArea(
child: Column(
children: <Widget>[
SizedBox(height: 32),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: textFieldSearch(),
),
SizedBox(height: 32),
Expanded(child: productList(products))
],
),
),
);
}
Future<void> onRefresh() async {
await Future<void>.delayed(const Duration(seconds: 3));
setState(() {
futureProducts = ProductService.fetchAll();
});
}
TextField textFieldSearch() {
OutlineInputBorder outlineInputBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
);
IconButton iconButton = IconButton(
onPressed: () {
setState(() {
futureProducts =
ProductService.search({"search": searchController.text});
});
},
icon: Icon(Icons.search),
);
InputDecoration decoration = InputDecoration(
labelText: "Search",
border: outlineInputBorder,
suffixIcon: iconButton,
);
return TextField(
controller: searchController,
decoration: decoration,
);
}
ListView productList(List<Product> products) {
return ListView.separated(
itemBuilder: (BuildContext build, int index) {
return productDetail(products[index]);
},
separatorBuilder: (BuildContext context, int index) => const Divider(),
itemCount: products.length,
);
}
ListTile productDetail(Product product) {
return ListTile(
title: Text(product.title),
subtitle: Text(product.description),
trailing: Text(product.price),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ProductUpdateScreen(id: product.id);
}));
},
);
}
}
// lib/screens/product_update_screen.dart
import 'package:crud_flutter_rest/models/product.dart';
import 'package:crud_flutter_rest/services/product_service.dart';
import 'package:crud_flutter_rest/widgets/componentes.dart';
import 'package:crud_flutter_rest/widgets/values.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class ProductUpdateScreen extends StatefulWidget {
const ProductUpdateScreen({super.key, required this.id});
final String id;
@override
State<ProductUpdateScreen> createState() => _ProductUpdateScreenState();
}
class _ProductUpdateScreenState extends State<ProductUpdateScreen> {
TextEditingController titleController = TextEditingController();
TextEditingController imageController = TextEditingController(
text: "https://loremflickr.com/640/480/animals",
);
TextEditingController descriptionController = TextEditingController(
text: "Andy shoes are designed to keeping "
"in mind durability as well as trends",
);
TextEditingController priceController = TextEditingController(text: "34.90");
Future<Product>? futureProduct;
@override
void initState() {
super.initState();
futureProduct = ProductService.fetchOne(widget.id);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: _header(),
body: buildFutureBuilder(),
);
}
void _initController(Product product) {
titleController.text = product.title;
imageController.text = product.image;
descriptionController.text = product.description;
priceController.text = product.price;
}
AppBar _header() {
return AppBar(
centerTitle: true,
title: Text('Update Product'),
);
}
SingleChildScrollView buildLayout() {
return SingleChildScrollView(
child: Column(
children: <Widget>[
sh32(),
ph16(_title()),
sh32(),
ph16(_image()),
sh32(),
ph16(_description()),
sh32(),
ph16(_price()),
sh32(),
ph16(_save()),
sh32()
],
),
);
}
TextField _title() {
OutlineInputBorder outlineInputBorder =
OutlineInputBorder(borderRadius: BorderRadius.circular(10));
InputDecoration decoration = InputDecoration(
helperText: "Please input your product title",
labelText: "Title",
border: outlineInputBorder);
return TextField(
controller: titleController,
decoration: decoration,
);
}
TextField _image() {
OutlineInputBorder outlineInputBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
);
InputDecoration decoration = InputDecoration(
helperText: "Please input image URL like https://...",
labelText: "Image (url)",
border: outlineInputBorder,
);
return TextField(
keyboardType: TextInputType.url,
controller: imageController,
decoration: decoration,
);
}
TextField _description() {
OutlineInputBorder outlineInputBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
);
InputDecoration decoration = InputDecoration(
alignLabelWithHint: true,
labelText: "Description",
helperText: "Please input your product description",
border: outlineInputBorder);
return TextField(
maxLines: 4,
controller: descriptionController,
decoration: decoration,
);
}
TextField _price() {
OutlineInputBorder outlineInputBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
);
InputDecoration decoration = InputDecoration(
helperText: "Please input your product price",
labelText: "Price",
border: outlineInputBorder,
);
return TextField(
keyboardType: TextInputType.number,
controller: priceController,
decoration: decoration,
);
}
TextButton _save() {
return TextButton(
onPressed: () {
setState(() {
futureProduct = ProductService.update(
widget.id,
titleController.text,
imageController.text,
priceController.text,
descriptionController.text,
);
futureProduct?.whenComplete(() {
info(context, "Sucess updating product.");
Navigator.pop(context);
});
});
},
style: TextButton.styleFrom(
backgroundColor: Colors.blue,
minimumSize: Size(double.infinity, 30),
padding: EdgeInsets.symmetric(vertical: 16),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
),
child: Text("Save",
style: TextStyle(
fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white)),
);
}
FutureBuilder<Product> buildFutureBuilder() {
return FutureBuilder<Product>(
future: futureProduct,
builder: (BuildContext context, AsyncSnapshot<Product> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
Product product = snapshot.data!;
_initController(product);
return buildLayout();
} else if (snapshot.hasError) {
return Center(child: Text(snapshot.error.toString()));
}
}
return Center(child: CircularProgressIndicator());
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment