Skip to content

Instantly share code, notes, and snippets.

@danielsmykowski1
Created November 26, 2019 05:23
Show Gist options
  • Save danielsmykowski1/8b82e013e303b59adf3a2e905f8f7f8f to your computer and use it in GitHub Desktop.
Save danielsmykowski1/8b82e013e303b59adf3a2e905f8f7f8f to your computer and use it in GitHub Desktop.
A Flutter component using Bloc pattern
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:image_picker/image_picker.dart';
import 'package:lumbungrempah_common/bloc/commodity/commodity.dart';
import 'package:lumbungrempah_common/model/commodity.dart';
import 'package:lumbungrempah_flutter/api/commodity_service_api_impl.dart';
import 'package:lumbungrempah_flutter/api/storage_service_api_impl.dart';
import 'package:lumbungrempah_flutter/screens/GetLocation.dart';
import 'package:lumbungrempah_flutter/utils/widget_utils.dart';
import 'package:lumbungrempah_flutter/widgets/loading_view.dart';
class CommodityEdit extends StatefulWidget {
final String qrCode;
CommodityEdit({Key key, @required this.qrCode}) : super(key: key);
@override
_CommodityEditState createState() {
return _CommodityEditState();
}
}
class _CommodityEditState extends State<CommodityEdit> {
CommodityBloc _screenBloc;
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
TextEditingController _nameController = TextEditingController();
TextEditingController _descController = TextEditingController();
TextEditingController _priceController = TextEditingController();
TextEditingController _unitController = TextEditingController();
TextEditingController _quantityController = TextEditingController();
String errorMessage;
bool isError = false;
File _image;
String _imageUrl;
num _longitude, _latitude;
String _address;
@override
void initState() {
_screenBloc = CommodityBloc(CommodityServiceApiImpl());
_screenBloc.dispatch(LoadInitialData(qrCode: widget.qrCode));
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocListener(
bloc: _screenBloc,
listener: (BuildContext context, CommodityState state) {
print('state: $state');
_imageUrl = null;
if (state is CommodityFailure) {
showErrorSnackbar(_scaffoldKey, state.error);
} else if (state is CommoditySaveSuccess) {
Navigator.of(context).pop('Commodity saved successfully.');
} else if (state.commodity != null) {
_nameController.text = state.commodity.name;
_descController.text = state.commodity.desc;
_priceController.text = state.commodity.price.toString();
_unitController.text = state.commodity.unit;
_quantityController.text = state.commodity.quantity.toString();
_imageUrl = state.commodity.imageUrl;
_longitude = null;
_latitude = state.commodity.latitude;
_address = state.commodity.address;
}
},
child: BlocBuilder<CommodityBloc, CommodityState>(
bloc: _screenBloc,
builder: (BuildContext context, state) {
return Scaffold(
appBar: AppBar(
title: Text(state.commodity == null
? 'Create Commodity'
: 'Update Commodity'),
actions: <Widget>[
FlatButton(
child: Text(
'Save',
style: TextStyle(color: Colors.white),
),
onPressed: state.isLoading
? null
: () async {
if (_isValidate()) {
_scaffoldKey.currentState.showSnackBar(SnackBar(
duration: Duration(minutes: 5),
content: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text('Loading...'),
CircularProgressIndicator(),
],
),
));
await _onSaveButtonPressed(state);
} else {
setState(() {
isError = true;
});
}
},
)
],
),
key: _scaffoldKey,
body: _getBody(state),
);
},
),
);
}
bool _isValidate() {
if (_nameController.text.trim().isEmpty ||
_descController.text.trim().isEmpty ||
_priceController.text.trim().isEmpty ||
_unitController.text.trim().isEmpty ||
_quantityController.text.trim().isEmpty) {
errorMessage = 'Please fill all required fields';
return false;
} else {
num n = num.tryParse(_priceController.text.trim());
if (n == null) {
errorMessage = 'Please enter a number for price';
return false;
}
n = num.tryParse(_quantityController.text.trim());
if (n == null) {
errorMessage = 'Please enter a number for quantity';
return false;
}
return true;
}
}
Future _onSaveButtonPressed(CommodityState state) async {
if (_image != null) {
_imageUrl =
await StorageServiceApiImpl().uploadFile('commodityimage', _image);
print('uploaded image url: $_imageUrl');
}
Commodity commodity = state.commodity;
if (commodity == null) {
commodity = Commodity(
name: _nameController.text.trim(),
desc: _descController.text.trim(),
price: num.parse(_priceController.text.trim()),
unit: _unitController.text.trim(),
quantity: num.parse(_quantityController.text.trim()),
kapuToken: widget.qrCode,
imageUrl: _imageUrl,
);
_screenBloc.dispatch(CreateCommodity(commodity: commodity));
} else {
commodity.name = _nameController.text.trim();
commodity.desc = _descController.text.trim();
commodity.price = num.parse(_priceController.text.trim());
commodity.unit = _unitController.text.trim();
commodity.quantity = num.parse(_quantityController.text.trim());
commodity.updatedAt = DateTime.now().toUtc();
commodity.imageUrl = _imageUrl;
_screenBloc.dispatch(UpdateCommodity(commodity: commodity));
}
}
Widget _getBody(CommodityState state) {
if (state.isLoading) {
print('Loading');
return LoadingView();
} else {
return ListView(
children: <Widget>[
_error(),
_getTextField(
title: '*Name:',
controller: _nameController,
validator: (String value) {
if (value.trim().isEmpty) {
return 'Please enter commodity name';
} else {
return null;
}
}),
_getImageField(state),
_getTextField(
title: '*Description:',
controller: _descController,
lines: 3,
validator: (String value) {
if (value.trim().isEmpty) {
return 'Please enter commodity description';
} else {
return null;
}
}),
_getTextField(
title: '*Price:',
controller: _priceController,
isNumber: true,
validator: (String value) {
if (value.trim().isEmpty) {
return 'Please enter commodity price';
} else {
final n = num.tryParse(value.trim());
if (n == null) {
return 'Please enter a number for price';
}
return null;
}
}),
_getTextField(
title: '*Unit:',
controller: _unitController,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter commodity unit';
} else {
return null;
}
}),
_getTextField(
title: '*Quantity:',
controller: _quantityController,
isNumber: true,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter commodity quantity';
} else {
final n = num.tryParse(value.trim());
if (n == null) {
return 'Please enter a number for quantity';
}
return null;
}
}),
_getLocationField(state),
],
);
}
}
Widget _error() {
return (isError)
? Padding(
padding: const EdgeInsets.all(10.0),
child: Center(
child: Text(
errorMessage,
style: TextStyle(color: Colors.red, fontSize: 16),
),
),
)
: Container();
}
Widget _getTextField(
{String title,
TextEditingController controller,
int lines = 1,
bool isNumber = false,
String Function(String) validator}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Text(title),
),
TextFormField(
validator: validator,
autovalidate: isError,
decoration: InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.all(10)),
controller: controller,
maxLines: lines,
keyboardType: isNumber ? TextInputType.number : TextInputType.text,
),
],
),
);
}
Widget _getImageField(CommodityState state) {
print('_getImageField, imageUrl: $_imageUrl');
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
width: 200,
height: 150,
decoration: BoxDecoration(
border: Border.all(
width: 2,
color: Colors.blue,
),
),
child: (_image != null)
? Image.file(_image, fit: BoxFit.cover)
: ((_imageUrl != null && _imageUrl != '')
? Image.network(_imageUrl, fit: BoxFit.cover)
: Image.asset(
'assets/placeholder.png',
fit: BoxFit.cover
)),
margin: EdgeInsets.only(right: 10),
),
Column(
children: <Widget>[
IconButton(
icon: Icon(Icons.image),
onPressed: () {
getImage(false);
},
iconSize: 40,
),
IconButton(
icon: Icon(Icons.camera_alt),
onPressed: () {
getImage(true);
},
iconSize: 40,
),
],
)
],
),
);
}
Future getImage(bool isCamera) async {
var image = await ImagePicker.pickImage(
source: isCamera ? ImageSource.camera : ImageSource.gallery);
setState(() {
_image = image;
});
}
Widget _getLocationField(CommodityState state) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text('Location'),
),
Padding(
padding: EdgeInsets.only(bottom: 10, left: 20),
child: Text(
'Longitude: ${_longitude == null ? 'unset' : _longitude}',
style: TextStyle(
fontSize: 17,
),
),
),
Padding(
padding: EdgeInsets.only(bottom: 10, left: 20),
child: Text(
'Latitude : ${_latitude == null ? 'unset' : _latitude}',
style: TextStyle(
fontSize: 17,
),
),
),
Padding(
padding: EdgeInsets.only(bottom: 10, left: 20),
child: Text(
'Address : ${_address == null ? 'unset' : _longitude}',
style: TextStyle(
fontSize: 17,
),
),
),
Container(
padding: EdgeInsets.only(left: 20),
child: RaisedButton(
onPressed: _onGetLocationPressed,
child: Text(
'Get Location',
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
padding: EdgeInsets.only(left: 20, right: 20),
color: Colors.blue,
),
),
],
),
);
}
void _onGetLocationPressed() async {
String result = await Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => GetLocation(
latitude: _latitude,
longitude: _longitude,
address: _address,
),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment