By clicking on save button of the form, user either updates and existing item or add a new one. For that, in _saveForm() method, I have implemented the following if statement, but it doesn't work properly. It just updates the already-existing item and when it comes to add a new one, It does not. What is the problem?
Future<void> _saveForm() async {
final isValid = _form.currentState!.validate();
if (!isValid) {
return;
}
_form.currentState!.save();
setState(() {
_isLoading = true;
});
if (_editedItem.id != null) {
await Provider.of<Items>(context, listen: false)
.updateItem(_editedItem.id.toString(), _editedItem);
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
} else {
try {
await Provider.of<Items>(context, listen: false).addItem(_editedItem);
} catch (error) {
await showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('An error occurred!'),
content: const Text('Something went wrong.'),
actions: <Widget>[
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(ctx).pop();
},
)
],
),
);
}
}
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
}
Followings are the methods called inside _saveForm() function:
Future<void> updateItem(String id, Item newItem) async {
final itemIndex = _items.indexWhere((item) => item.id == id);
if (itemIndex >= 0) {
final url = 'https://shaparak-732ff.firebaseio.com/items/$id.json';
await http.patch(Uri.parse(url),
body: json.encode({
'title': newItem.title,
'content': newItem.content,
'author': newItem.author,
'category': newItem.category,
'startColor': newItem.startColor,
'endColor': newItem.endColor,
}));
_items[itemIndex] = newItem;
notifyListeners();
} else {
print('...');
}
}
And the method that does not function:
Future<void> addItem(Item item) {
const url = 'https://shaparak-732ff.firebaseio.com/items.json';
return http
.post(
Uri.parse(url),
body: json.encode({
'title': item.title,
'content': item.content,
'author': item.author,
'category': item.category,
'startColor': item.startColor,
'endColor': item.endColor,
}),
)
.then((response) {
final newItem = Item(
title: item.title,
content: item.content,
author: item.author,
category: item.category,
startColor: item.startColor,
endColor: item.endColor,
id: json.decode(response.body)['name'],
);
_items.add(newItem);
// _items.insert(0, newProduct); // at the start of the list
notifyListeners();
});
}
I think you don't add your product id, you will follow this code .....
Future<void> addItem(Item item) {
const url = 'https://shaparak-732ff.firebaseio.com/items.json';
return http
.post(
Uri.parse(url),
body: json.encode({
'id': item.id,
'title': item.title,
'content': item.content,
'author': item.author,
'category': item.category,
'startColor': item.startColor,
'endColor': item.endColor,
}),
)
.then((response) {
final newItem = Item(
id: json.decode(response.body)['name'],
title: item.title,
content: item.content,
author: item.author,
category: item.category,
startColor: item.startColor,
endColor: item.endColor
);
_items.insert(0, newItem);
notifyListeners();
});
}
Hey basically your problem looks like it might be because id can never be null. So instead of
if (_editedItem.id != null) {
await Provider.of<Items>(context, listen: false)
.updateItem(_editedItem.id.toString(), _editedItem);
setState(() {
_isLoading = false;
});
try making it say
if (_editedItem.id != '') {
await Provider.of<Items>(context, listen: false)
.updateItem(_editedItem.id.toString(), _editedItem);
setState(() {
_isLoading = false;
});
Related
I am developing a voice/video-calling app using Flutter and Agora rtc engine (v 5.3.1). I have followed all the necessary steps for generating token and joining a channel. But, I can't see the local camera view in the UI and the logs gives me warning saying: "onWarning warn 8 msg invalid view for local video". Any leads would be big help, thanks in advance.
Logs:
W/spdlog (30579): [2023-01-04 21:02:33.375] [0] [warning] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:43 onWarning warn 8 msg invalid view for local video
W/spdlog (30579): [2023-01-04 21:02:33.375] [0] [warning] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:43 onWarning warn 16
msg nullptr
I/spdlog (30579): [2023-01-04 21:02:33.375] [0] [info] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:72 onJoinChannelSuccess
channel b8667da0-8c6a-11ed-a9fb-578e8ad35bd6 uid 1
W/spdlog (30579): [2023-01-04 21:02:33.377] [0] [warning] /tmp/jenkins/IRIS-SDK/rtc/cxx/src/internal/rtc_engine_event_handler.cc:43 onWarning warn 16
msg nullptr
Call Screen Widget:
imports...
String baseUrl = 'https://...';
class CallScreen extends ConsumerStatefulWidget {
final Call call;
const CallScreen({Key? key, required this.call, }) : super(key: key);
#override
_CallScreenState createState() => _CallScreenState();
}
class _CallScreenState extends ConsumerState<CallScreen> {
int uid = 1;
List<int> _remoteUids = [];
bool isJoined = false,
switchCamera = true,
openMicrophone = true,
enableSpeakerphone = false;
late bool openCamera;
late RtcEngine _engine;
#override
void initState() {
initAgora();
openCamera = widget.call.isVideoCall;
super.initState();
}
Future<String?> _getToken() async {
String url = baseUrl + "/rtc/" + widget.call.callId + "/publisher/uid/" + uid.toString() + "/";;
String? token;
try{
var resp = await http.get(Uri.parse(url));
if(resp.statusCode == 200){
token = jsonDecode(resp.body)['rtcToken'];
return token;
}
else{
showMySnackBar(context: context, content: "Token Status ERR: "+jsonDecode(resp.body)['message']);
return null;
}
}
catch(e){
showMySnackBar(context: context, content: "Token Err: "+e.toString());
return null;
}
}
void _joinChannel() async {
String? token = await _getToken();
if(token != null){
await _engine.joinChannel(token, widget.call.callId, null, uid);
}
else{
showMySnackBar(context: context, content: 'Token is null!');
}
}
void _leaveChannel() async {
ref.read(callControllerProvider).endCall(
widget.call.callerId,
widget.call.receiverId,
context,
widget.call.isGroupChat
);
if(widget.call.isVideoCall) await _engine.stopPreview();
await _engine.leaveChannel();
}
void _switchCamera() {
_engine.switchCamera()
.then((value) {
setState(() {
switchCamera = !switchCamera;
});
})
.catchError((err) {
//print('switchCamera $err');
});
}
void _switchMicrophone() async {
// await _engine.muteLocalAudioStream(!openMicrophone);
await _engine.enableLocalAudio(!openMicrophone)
.then((value) {
setState(() {
openMicrophone = !openMicrophone;
});
})
.catchError((err) {
// print('enableLocalAudio $err');
});
}
void _switchSpeakerphone() {
_engine.setEnableSpeakerphone(!enableSpeakerphone)
.then((value) {
setState(() {
enableSpeakerphone = !enableSpeakerphone;
});
})
.catchError((err) {
//print('setEnableSpeakerphone $err');
});
}
void initAgora() async {
try{
await [Permission.microphone, Permission.camera].request();
_engine = await RtcEngine.createWithContext(RtcEngineContext(AgoraConfig.appId));
_engine.setEventHandler(
RtcEngineEventHandler(
warning: (warn) {
showMySnackBar(context: context, content: "Warn: "+warn.name);
},
error: (err) {
showMySnackBar(context: context, content: 'OnErr event: '+err.name);
},
joinChannelSuccess: (String channel, int userId, int elapsed) {
// print("local user ${connection.localUid} joined");
if(mounted){
setState(() {
isJoined = true;
uid = userId;
});
}
showMySnackBar(context: context, content: 'You ($userId) have joined the call!');
},
userJoined: (int remoteUid, int elapsed) {
//debugPrint("remote user $remoteUid joined");
if(mounted){
setState(() {
_remoteUids.add(remoteUid);
});
}
},
userOffline: (int remoteUid, UserOfflineReason reason) {
//debugPrint("remote user $remoteUid left channel");
if(mounted){
setState(() {
_remoteUids.removeWhere((element) => element == remoteUid);
});
}
},
leaveChannel: (stats) {
if(mounted){
setState(() {
isJoined = false;
if(!widget.call.isGroupChat || _remoteUids.length == 1){
_remoteUids.clear();
}
});
}
},
// onTokenPrivilegeWillExpire: (RtcConnection connection, String token) {
// debugPrint('[onTokenPrivilegeWillExpire] connection: ${connection.toJson()}, token: $token');
// },
),
);
await _engine.setChannelProfile(ChannelProfile.LiveBroadcasting);
//await _engine.setClientRole(ClientRole.Broadcaster);
await _engine.enableVideo();
if(widget.call.isVideoCall){
await _engine.startPreview();
}
else{
await _engine.muteLocalVideoStream(true);
await _engine.muteAllRemoteVideoStreams(true);
}
_joinChannel();
}
catch(e){
showMySnackBar(context: context, content: "Init Err: "+e.toString());
}
}
#override
void dispose() {
_leaveChannel();
_engine.destroy();
super.dispose();
}
// Display remote user's video
Widget _remoteVideo() {
if (_remoteUids.isNotEmpty) {
//TODO check for video on or off or if video call:
return rtc_remote_view.SurfaceView(
uid: _remoteUids[0],
channelId: widget.call.callId,
);
}
else {
return const Text(
'Please wait for others to join',
textAlign: TextAlign.center,
);
}
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async { _leaveChannel(); return true; },
child: Scaffold(
backgroundColor: Colors.white,
body: Stack(
children: [
Center(
child: _remoteVideo(),
),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(right: 18.0, bottom: 12),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(12)),
child: ColoredBox(
color: Colors.grey.shade200,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 125, maxHeight: 175),
child: AspectRatio(
aspectRatio: 9/16,
child: Center(
child: isJoined
?
( //TODO: check for video on or off or if video call:
openCamera
? rtc_local_view.SurfaceView(
channelId: widget.call.callId,
)
: const Icon(
Icons.videocam_off_rounded,
color: Colors.black,
size: appActionsIconsSize,
)
)
: const CircularProgressIndicator(),
),
),
),
),
),
),
),
],
),
),
);
}
}
I found the issue: I was not setting the ClientRoleType correctly and that caused error in finding local view. One needs to define ClientRoleType (based on your logic) and ChannelProfileType.broadcast and everything seems to work.
I'm trying to store data that fetched from API using sqflite
Now I'm getting an error saying...
A value of type 'List' can't be returned from the method 'getAllEmployees' because it has a return type of 'Future<List>'.
Here's my employee_provider.dart file
class EmployeeApiProvider {
Future<List<Employee>> getAllEmployees() async {
var url = "http://demo8161595.mockable.io/employee";
var response = await Dio().get(url);
return employeeFromJson(response.data).map((employee) {
DBProvider.db.createEmployee(employee);
}).toList();
}
}
How to fix this?
And other files relevant to this are...
db_provider.dart
class DBProvider {
static Database? _database;
static final DBProvider db = DBProvider._();
DBProvider._();
Future<Database?> get database async {
if (_database != null) return _database;
_database = await initDB();
return _database;
}
initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, 'employee_manager.db');
return await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database db, int version) async {
await db.execute('CREATE TABLE Employee('
'id INTEGER PRIMARY KEY,'
'email TEXT,'
'firstName TEXT,'
'lastName TEXT,'
'avatar TEXT'
')');
});
}
createEmployee(Employee newEmployee) async {
await deleteAllEmployees();
final db = await database;
final res = await db!.insert('Employee', newEmployee.toJson());
return res;
}
Future<int> deleteAllEmployees() async {
final db = await database;
final res = await db!.rawDelete('DELETE FROM Employee');
return res;
}
Future<List<Employee>> getAllEmployees() async {
final db = await database;
final res = await db!.rawQuery("SELECT * FROM EMPLOYEE");
List<Employee> list =
res.isNotEmpty ? res.map((c) => Employee.fromJson(c)).toList() : [];
return list;
}
}
employee_model.dart
List<Employee> employeeFromJson(String str) =>
List<Employee>.from(json.decode(str).map((x) => Employee.fromJson(x)));
String employeeToJson(List<Employee> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Employee {
int id;
String email;
String firstName;
String lastName;
String avatar;
Employee({
required this.id,
required this.email,
required this.firstName,
required this.lastName,
required this.avatar,
});
factory Employee.fromJson(Map<String, dynamic> json) => Employee(
id: json["id"],
email: json["email"],
firstName: json["firstName"],
lastName: json["lastName"],
avatar: json["avatar"],
);
Map<String, dynamic> toJson() => {
"id": id,
"email": email,
"firstName": firstName,
"lastName": lastName,
"avatar": avatar,
};
}
home.dart
class _HomeState extends State<AbcView> {
var isLoading = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Api to sqlite'),
centerTitle: true,
actions: <Widget>[
Container(
padding: const EdgeInsets.only(right: 10.0),
child: IconButton(
icon: const Icon(Icons.settings_input_antenna),
onPressed: () async {
await _loadFromApi();
},
),
),
Container(
padding: const EdgeInsets.only(right: 10.0),
child: IconButton(
icon: const Icon(Icons.delete),
onPressed: () async {
await _deleteData();
},
),
),
],
),
body: isLoading
? const Center(
child: CircularProgressIndicator(),
)
: _buildEmployeeListView(),
);
}
_loadFromApi() async {
setState(() {
isLoading = true;
});
var apiProvider = EmployeeApiProvider();
await apiProvider.getAllEmployees();
await Future.delayed(const Duration(seconds: 2));
setState(() {
isLoading = false;
});
}
_deleteData() async {
setState(() {
isLoading = true;
});
await DBProvider.db.deleteAllEmployees();
await Future.delayed(const Duration(seconds: 1));
setState(() {
isLoading = false;
});
}
_buildEmployeeListView() {
return FutureBuilder(
future: DBProvider.db.getAllEmployees(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.separated(
separatorBuilder: (context, index) => const Divider(
color: Colors.black12,
),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: Text(
"${index + 1}",
style: const TextStyle(fontSize: 20.0),
),
title: Text(
"Name: ${snapshot.data[index].firstName} ${snapshot.data[index].lastName} "),
subtitle: Text('EMAIL: ${snapshot.data[index].email}'),
);
},
);
}
},
);
}
}
Maybe you could try to add await in front of the DBProvider.db.createEmployee?
class EmployeeApiProvider {
Future<List<Employee>> getAllEmployees() async {
var url = "http://demo8161595.mockable.io/employee";
var response = await Dio().get(url);
return employeeFromJson(response.data).map((employee) async {
await DBProvider.db.createEmployee(employee);
}).toList();
}
}
I am creating a simple Shop app where I implement logic of CircularProgressIndicator() When user add new product. And beside that to show AlertDialog When some error occurred. But the problem is Whenever I pressed Okay button on AlertDialog instead of going back to Products page the CircularProgressIndicator() keeps spinning.
Here is My code:
class EditProductScreen extends StatefulWidget {
static const routname = '/edit-product-screen';
const EditProductScreen({Key? key}) : super(key: key);
#override
State<EditProductScreen> createState() => _EditProductScreenState();
}
class _EditProductScreenState extends State<EditProductScreen> {
final _priceFocusNode = FocusNode();
final _descriptionFocusNode = FocusNode();
final _imageUrlController = TextEditingController();
final _imageUrlFocusNode = FocusNode();
final _form = GlobalKey<FormState>();
var _editedProduct = Product(
id: null,
title: '',
price: 0,
description: '',
imageUrl: '',
);
var _initValues = {
'title': '',
'description': '',
'price': '',
'imageUrl': '',
};
var _isInit = true;
var _isLoading = false;
#override
void initState() {
_imageUrlFocusNode.addListener(_updateImageUrl);
super.initState();
}
#override
void didChangeDependencies() {
if (_isInit) {
final productId = ModalRoute.of(context)!.settings.arguments;
// ignore: unnecessary_null_comparison
if (productId != null) {
_editedProduct = Provider.of<ProductsProvider>(context, listen: false)
.findById(productId as String);
_initValues = {
'title': _editedProduct.title,
'description': _editedProduct.description,
'price': _editedProduct.price.toString(),
// 'imageUrl': _editedProduct.imageUrl,
'imageUrl': '',
};
_imageUrlController.text = _editedProduct.imageUrl;
}
}
_isInit = false;
super.didChangeDependencies();
}
#override
void dispose() {
_imageUrlFocusNode.removeListener(_updateImageUrl);
_priceFocusNode.dispose();
_descriptionFocusNode.dispose();
_imageUrlController.dispose();
_imageUrlFocusNode.dispose();
super.dispose();
}
void _updateImageUrl() {
if (!_imageUrlFocusNode.hasFocus) {
if ((!_imageUrlController.text.startsWith('http') &&
!_imageUrlController.text.startsWith('https')) ||
(!_imageUrlController.text.endsWith('.png') &&
!_imageUrlController.text.endsWith('.jpg') &&
!_imageUrlController.text.endsWith('.jpeg'))) {
return;
}
setState(() {});
}
}
void _saveForm() {
final isValid = _form.currentState!.validate();
if (!isValid) {
return;
}
_form.currentState!.save();
setState(() {
_isLoading = true;
});
// ignore: unnecessary_null_comparison
if (_editedProduct.id != null) {
Provider.of<ProductsProvider>(context, listen: false)
.updateProduct(_editedProduct.id!, _editedProduct);
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
} else {
Provider.of<ProductsProvider>(context, listen: false)
.addProduct(_editedProduct)
.catchError((onError) {
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text("An Error Occourrd"),
content: const Text("Something Went Wrong"),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: const Text("Okay"),
)
],
),
);
}).then((_) {
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Edit Product'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.save),
onPressed: _saveForm,
),
],
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _form,
child: ListView(
children: <Widget>[
TextFormField(
initialValue: _initValues['title'],
decoration: const InputDecoration(labelText: 'Title'),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
FocusScope.of(context).requestFocus(_priceFocusNode);
},
validator: (value) {
if (value!.isEmpty) {
return 'Please Enter a Title';
}
return null;
},
onSaved: (value) {
_editedProduct = Product(
title: value.toString(),
price: _editedProduct.price,
description: _editedProduct.description,
imageUrl: _editedProduct.imageUrl,
id: _editedProduct.id,
isFav: _editedProduct.isFav);
},
),
TextFormField(
initialValue: _initValues['price'],
decoration: const InputDecoration(labelText: 'Price'),
textInputAction: TextInputAction.next,
keyboardType: TextInputType.number,
focusNode: _priceFocusNode,
onFieldSubmitted: (_) {
FocusScope.of(context)
.requestFocus(_descriptionFocusNode);
},
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a price.';
}
if (double.tryParse(value) == null) {
return 'Please enter a valid number.';
}
if (double.parse(value) <= 0) {
return 'Please enter a number greater than zero.';
}
return null;
},
onSaved: (value) {
_editedProduct = Product(
title: _editedProduct.title,
price: int.parse(value.toString()),
description: _editedProduct.description,
imageUrl: _editedProduct.imageUrl,
id: _editedProduct.id,
isFav: _editedProduct.isFav);
},
),
TextFormField(
initialValue: _initValues['description'],
decoration:
const InputDecoration(labelText: 'Description'),
maxLines: 3,
keyboardType: TextInputType.multiline,
focusNode: _descriptionFocusNode,
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a description.';
}
if (value.length < 10) {
return 'Should be at least 10 characters long.';
}
return null;
},
onSaved: (value) {
_editedProduct = Product(
title: _editedProduct.title,
price: _editedProduct.price,
description: value.toString(),
imageUrl: _editedProduct.imageUrl,
id: _editedProduct.id,
isFav: _editedProduct.isFav,
);
},
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
width: 100,
height: 100,
margin: const EdgeInsets.only(
top: 8,
right: 10,
),
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: Colors.grey,
),
),
child: _imageUrlController.text.isEmpty
? const Text('Enter a URL')
: FittedBox(
child: Image.network(
_imageUrlController.text,
fit: BoxFit.cover,
),
),
),
Expanded(
child: TextFormField(
decoration:
const InputDecoration(labelText: 'Image URL'),
keyboardType: TextInputType.url,
textInputAction: TextInputAction.done,
controller: _imageUrlController,
focusNode: _imageUrlFocusNode,
onFieldSubmitted: (_) {
_saveForm();
},
validator: (value) {
if (value!.isEmpty) {
return 'Please enter an image URL.';
}
if (!value.startsWith('http') &&
!value.startsWith('https')) {
return 'Please enter a valid URL.';
}
if (!value.endsWith('.png') &&
!value.endsWith('.jpg') &&
!value.endsWith('.jpeg')) {
return 'Please enter a valid image URL.';
}
return null;
},
onSaved: (value) {
_editedProduct = Product(
title: _editedProduct.title,
price: _editedProduct.price,
description: _editedProduct.description,
imageUrl: value.toString(),
id: _editedProduct.id,
isFav: _editedProduct.isFav,
);
},
),
),
],
),
],
),
),
),
);
}
}
Here is My Product Provider Class Code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import './product.dart';
class ProductsProvider with ChangeNotifier {
// ignore: prefer_final_fields
List<Product> _items = [
Product(
id: 'p1',
title: 'Red Shirt',
description: 'A red shirt - it is pretty red!',
price: 29,
imageUrl:
'https://cdn.pixabay.com/photo/2016/10/02/22/17/red-t-shirt-1710578_1280.jpg',
),
Product(
id: 'p2',
title: 'Trousers',
description: 'A nice pair of trousers.',
price: 59,
imageUrl:
'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Trousers%2C_dress_%28AM_1960.022-8%29.jpg/512px-Trousers%2C_dress_%28AM_1960.022-8%29.jpg',
),
Product(
id: 'p3',
title: 'Yellow Scarf',
description: 'Warm and cozy - exactly what you need for the winter.',
price: 19,
imageUrl:
'https://live.staticflickr.com/4043/4438260868_cc79b3369d_z.jpg',
),
Product(
id: 'p4',
title: 'A Pan',
description: 'Prepare any meal you want.',
price: 49,
imageUrl:
'https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Cast-Iron-Pan.jpg/1024px-Cast-Iron-Pan.jpg',
),
];
List<Product> get items {
// if (_showFavoritesOnly) {
// return _items.where((prodItem) => prodItem.isFavorite).toList();
// }
return [..._items];
}
List<Product> get favoriteItems {
return _items.where((prodItem) => prodItem.isFav).toList();
}
Product findById(String id) {
return _items.firstWhere((prod) => prod.id == id);
}
void showFavoritesOnly() {
notifyListeners();
}
void showAll() {
notifyListeners();
}
Future<void> addProduct(Product product) {
return http
.post(
Uri.parse(
'https://example.com/products.json'),
body: json.encode(
{
'title': product.title,
'description': product.description,
'price': product.price,
'imageUrl': product.imageUrl,
'isFav': product.isFav,
},
),
)
.then((rresponce) {
final newProduct = Product(
title: product.title,
description: product.description,
price: product.price,
imageUrl: product.imageUrl,
id: json.decode(rresponce.body)['name'],
);
_items.add(newProduct);
// _items.insert(0, newProduct); // at the start of the list
notifyListeners();
}).catchError((onError) {
throw onError;
});
}
void updateProduct(String id, Product newProduct) {
final prodIndex = _items.indexWhere((prod) => prod.id == id);
if (prodIndex >= 0) {
_items[prodIndex] = newProduct;
notifyListeners();
} else {}
}
void deleteProduct(String id) {
_items.removeWhere((prod) => prod.id == id);
notifyListeners();
}
}
The Navigator.of(ctx).pop(); will be used to close the dialog, in order to go back, you need to call again Navigator.of(context).pop();
try this way
void _saveForm() async {
final isValid = _form.currentState!.validate();
if (!isValid) {
return;
}
_form.currentState!.save();
setState(() {
_isLoading = true;
});
// ignore: unnecessary_null_comparison
if (_editedProduct.id != null) {
Provider.of<ProductsProvider>(context, listen: false)
.updateProduct(_editedProduct.id!, _editedProduct);
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
} else {
try {
await Provider.of<ProductsProvider>(context, listen: false)
.addProduct(_editedProduct);
setState(() {
_isLoading = false;
});
if (mounted) {
Navigator.of(context).pop();
}
} catch (e) {
await showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text("An Error Occourrd"),
content: const Text("Something Went Wrong"),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: const Text("Okay"),
)
],
),
);
print("tada");
setState(() {
_isLoading = false;
});
if (mounted) {
Navigator.of(context).pop();
}
}
}
}
The culprit here is a type inference mismatch, and you should have an error in your console:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument(s) (onError): The error handler of Future.catchError must return a value of the future's type
In your provider, the catchError returns a Future<Null>
Future<void> addProduct(Product value) {
var product = value;
final url = Uri.parse('${Config.firebaseUrl}/products');
return http
.post(url, body: ...)
.then((response) { // handle response }) // here returns nothing
.catchError((error) {throw error}); // so here returns Future<Null>
}
You can see it with Android Studio:
When in your Widget, the showDialog returns a Future<void>.
This is not seen at compile time, I suspect because of dynamic type inference, but at runtime, you have:
Provider.of<ProductsProvider>(context, listen: false)
.addProduct(_editedProduct) // Future<Null>
.catchError((onError) {
return showDialog(...);
}) // Future<void>
.then((_) { ... });
Thus the runtime error message.
To fix the error, simply delete the catchError block from your addProduct function. The error will still be thrown, but you will not have the type overwrite of catchError from Future<void> to Future<null> :
Future<void> addProduct(Product value) {
var product = value;
final url = Uri.parse('${Config.firebaseUrl}/products');
return http
.post(url, body: ...)
.then((response) { // handle response }); // removed catchError
}
I have App with login page have this function to login to server ,but when it fails to login the show Dialog did't be shown :
signup() async {
var formdata = formSignUpKey.currentState;
if (formdata.validate()) {
formdata.save();
var data = {
'username': username.text,
'password': password.text,
'email': email.text,
'nat_id': natId.text
};
var url =
"http://xxx/api/controller/users/register.php";
var response = await http.post(url, body: data);
var responsebody = jsonDecode(response.body);
if (responsebody['status'] == 'success') {
saveuser(responsebody['username'], responsebody['email']);
Navigator.of(context).pushReplacementNamed('index');
} else {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Error'),
content: Text(responsebody['status']),
actions: <Widget>[
FlatButton(
child: Text('Close!'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
}
}
}
I have tested it many time but never shown
I solved it after "Merym" asked to put functions inside try catch ,the code :
// Dialog error
_showDialog(context) {
return showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Error'),
content: Text('Username or password wrong'),
actions: <Widget>[
FlatButton(
child: Text('Close me!'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
}
//login function
login() async {
var formdata = formLoginKey.currentState;
if (formdata.validate()) {
formdata.save();
try {
var data = {'username': username.text, 'password': password.text};
var url =
"https://technosat-iq.com/myexpect/api/controller/users/login_user.php";
var response = await http.post(url, body: data);
var responsebody = jsonDecode(response.body);
print('wooow ' + responsebody['status']);
if (responsebody['status'] == 'success') {
saveuser(responsebody['username'], responsebody['email']);
Navigator.of(context).pushReplacementNamed('index');
} else {}
} catch (err) {
print('error = $err');
_showDialog(context);
}
} else {
print('Bad');
}
}
I need to display the rows data in the dropdown. Any help is appreciated.
class _MyAppState extends State<MyAppdrop> {
List<LeadService> payloadListdropdown;
bool loading = false;
String _mySelection;
List<LeadService> payloadFromJson(String str) =>
List<LeadService>.from(
json.decode(str).map((x) => LeadService.fromJson(x)));
String payloadToJson(List<LeadService> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
Future<List<LeadService>> requestMethodspinner() async {
var url = "";
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
final response = await http.post(url, headers: headers);
final responseJson = json.decode(response.body);
print(responseJson);
print(response.statusCode);
setState(() {
loading = true;
payloadListdropdown = payloadFromJson(response.body);
loading = false;
});
if (response.statusCode == 200) {}
else {
throw Exception('Failed to load internet');
}
return payloadListdropdown;
}
#override
void initState() {
super.initState();
this.requestMethodspinner();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("DropDown"),
),
body: new Center(
child: new DropdownButton(
items: payloadListdropdown.map((item) {
return new DropdownMenuItem(
child: new Text("${payloadListdropdown[0].rows[1].sName}"),
value: item[1].toString(),
);
}).toList(),
onChanged: (newVal) {
setState(() {
_mySelection = newVal;
});
},
value: _mySelection,
),
),
);
}
}
class LeadService {
int status;
String msg;
List<Rows> rows;
String sql;
dynamic reserved;
LeadService({
this.status,
this.msg,
this.rows,
this.sql,
this.reserved,
});
factory LeadService.fromJson(Map<String, dynamic> json) => LeadService(
status: json["status"],
msg: json["msg"],
rows: List<Rows>.from(json["rows"].map((x) => Rows.fromJson(x))),
sql: json["sql"],
reserved: json["reserved"],
);
Map<String, dynamic> toJson() => {
"status": status,
"msg": msg,
"rows": List<dynamic>.from(rows.map((x) => x.toJson())),
"sql": sql,
"reserved": reserved,
};
}
class Rows {
String ServicesId;
String sName;
String sCode;
Rows({
this.ServicesId,
this.sName,
this.sCode,
});
factory Rows.fromJson(Map<String, dynamic> json) => Rows(
//dropdownservicelist
ServicesId:json["ServicesId"],
sName:json["sName"],
sCode:json["sCode"],
);
Map<String, dynamic> toJson() => {
//dropdownservicelist
"ServicesId" :ServicesId,
"sName" : sName,
"sCode" : sCode,
};
}
Json data:
[
{
"status": 200,
"msg": "Ok",
"refcode": -1,
"trows": 13,
"rows": [
{
"ServicesId": "3",
"sName": "CFS",
"sCode": "CF"
},
{
"ServicesId": "2",
"sName": "Container aGENT",
"sCode": "CA"
},
{
"ServicesId": "1",
"sName": "Custom",
"sCode": "CB"
}
],
"sql": "Services",
"reserved": null
}
]
class _MyAppState extends State<MyAppdrop> {
Map payloadListdropdown;
Map _mySelection;
bool _loading = true;
requestMethodspinner() async {
var url = "";
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
final response = await http.post(url, headers: headers);
final responseJson = json.decode(response.body);
print(responseJson);
print(response.statusCode);
if (response.statusCode == 200) {
setState(() {
loading = false;
payloadListdropdown = json.decode(response.body)[0];
_mySelection = payloadListdropdown['rows'][0];
});
}
else {
setState((){
loading = false;
});
throw Exception('Failed to load internet');
}
}
#override
void initState() {
super.initState();
requestMethodspinner();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("DropDown"),
),
body: new Center(
child: _loading && payloadListdropdown == null ?
ListTile(
title: Text('Loading...'),
leading:CircularProgressIndicator(),
)
:
payloadListdropdown == null ?
ListTile(
title: Text('Internet not available or Nothing found !'),
)
: DropdownButton(
items: payloadListdropdown['rows'].map<DropDownMenuItem<Map>>((item) {
return new DropdownMenuItem(
child: new Text(item['sName']),
//Other fields like sCode etc..
value: item,
);
}).toList(),
onChanged: (newVal) {
setState(() {
_mySelection = newVal;
print(_mySelection.toString())
});
},
value: _mySelection,
),
),
);
}
}