Future and await no wait for flutter response - laravel

Let's see if someone can help me:
I am working with my own API, flutter and GETCONNECT.
I have noticed that if a query to the API takes more than 3 secs approx to respond, the response comes null and does not wait (does not do the await).
I have simplified all the code to be able to show it. If the API takes less than 3sec to respond, it makes the response, if it takes longer, it returns null.
If I connect to the API from another place it responds correctly.
I attach the code of the screen, the controller, the provider and the API.
Any suggestions? Thanks
SCREEN
class UtilsPage extends StatelessWidget {
UtilsController con = Get.put(UtilsController());
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(
title: 'Utils',
),
bottomNavigationBar: MyBottomNavigationBar(),
body: Center(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
width: MediaQuery.of(context).size.width * 0.60,
child: ElevatedButton(
onPressed: () => con.mandarFuture(),
style:
ElevatedButton.styleFrom(padding: const EdgeInsets.all(15)),
child: const Text(
'GO FUTURE',
style: TextStyle(color: Colors.white),
),
),
),
));
}
}
CONTROLLER
class UtilsController extends GetxController {
UsersProvider usersProvider = UsersProvider();
void mandarFuture() async {
ResponseApi responseApi = await usersProvider.prueba();
print('responseApi: ${responseApi.toJson()}');
}
}
PROVIDER
class UsersProvider extends GetConnect {
String url = Environment.API_URL + 'api';
User user = User.fromJson(GetStorage().read('user') ?? {});
Future<ResponseApi> prueba() async {
Response response = await get('${url}/prueba',
headers: {'Content-type': 'application/json'});
if (response.body == null) {
Get.snackbar('Error', 'No se pudo actualizar la información');
return ResponseApi();
}
if (response.statusCode == 401) {
Get.snackbar('Error', 'No estas autorizado');
return ResponseApi();
}
ResponseApi responseApi = ResponseApi.fromJson(response.body);
return responseApi;
}
API
public function prueba(){
for ($i=0; $i < 1000000000; $i++) {
# code...
}
return response()->json([
'message' => 'Mandangax! Prueba',
'data' => '',
'success' => true
]);
}

You need to use a FutureBuilder.
Something like this:
FutureBuilder(future: con.mandarFuture(), builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (!snapshot.hasData) {
return Text("No data from future");
}
final data = snapshot.data;
return Center(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
width: MediaQuery.of(context).size.width * 0.60,
child: ElevatedButton(
onPressed: () => doWhateverTheButtonHasToDo(),
style:
ElevatedButton.styleFrom(padding: const EdgeInsets.all(15)),
child: const Text(
'GO FUTURE',
style: TextStyle(color: Colors.white),
),
);
)
}

Related

Flutter FutureBuilder shows data then disappears when calling Rest API (Backend Laravel)

I'm running a flutter application with a laravel backend and I have some issues.
The problem is the FutureBuilder show data then it disappears; Sometimes length==4 then it turns to 0 and shows 'no data' in Scaffold🙄
The same when I refresh the code.
PS: I'm running laravel on localhost and using a real device to test.
Environment: Android Studio, Windows 10, Real device
Laravel project: https://github.com/brakenseddik/blog_api_laravel
Flutter project: https://github.com/brakenseddik/blog_api_flutter
import 'package:http/http.dart' as http;
class Repository {
String _baseUrl = 'http://192.168.1.2:8000/api';
httpGet(String api) async {
return await http.get(_baseUrl + '/' + api);
}
}
here the homepage source code
import 'dart:convert';
import 'package:blog_api/models/post_model.dart';
import 'package:blog_api/services/post_service.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
PostService _postService = PostService();
List<PostModel> _list = List<PostModel>();
Future<List<PostModel>> _getPosts() async {
var result = await _postService.getAllPosts();
_list = [];
if (result != null) {
var blogPosts = json.decode(result.body);
blogPosts.forEach((post) {
PostModel model = PostModel();
setState(() {
model.title = post['title'];
model.details = post['details'];
model.imageUrl = post['featured_image_url'];
_list.add(model);
});
});
}
return _list;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Blog App'),
),
body: FutureBuilder(
future: _getPosts(),
builder:
(BuildContext context, AsyncSnapshot<List<PostModel>> snapshot) {
print('length of list ${_list.length}');
_list = snapshot.data;
if (_list.length == 0) {
return Center(
child: Text('No data'),
);
} else if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
snapshot.data[index].imageUrl,
height: 150,
// width: double.maxFinite,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
snapshot.data[index].title,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w700),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
snapshot.data[index].details.substring(0, 25),
style: TextStyle(
fontSize: 16,
),
),
),
],
),
),
);
});
}
},
));
}
}
And the PostService
import 'package:blog_api/repository/repository.dart';
class PostService {
Repository _repository;
PostService() {
_repository = Repository();
}
getAllPosts() async {
return await _repository.httpGet('get-posts');
}
}
I think the issue is that you're calling setState in _getPosts(). This will rebuild everything and never give the response to the FutureBuilder. Just run the code without setState:
blogPosts.forEach((post) {
PostModel model = PostModel();
model.title = post['title'];
model.details = post['details'];
model.imageUrl = post['featured_image_url'];
_list.add(model);
FutureBuilder will initially call its builder with a snapshot that doesn't have any data yet. Once it receives the data, it will call its builder again. Because of this, if (_list.length == 0) will result in a NPE, since _list is null.
I would try changing the order of the if statements:
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.data.length == 0) {
return Center(
child: Text('No data'),
);
} else {
return ListView.builder(
//...
}
future: _getPosts(),
Don't do that. This means every time build is called, you are querying your API again.
Create a variable of type Future<List<PostModel>>, assign _getPosts() to it once and then use that variable with your FutureBuilder.

How to populate JSON data when id is passed from first activity to second activities

I'm new to flutter am developing contacts app using laravel backend i have two tables which are constrained on foreign key and json response works on postman. in Flutter i have two models users model and userdetail model. users name in list view like below and works fine
class ListUsers extends StatefulWidget {
#override
_ListItemsState createState() => _ListItemsState();
}
class _ListUsersState extends State<ListUsers> {
UsersApi usersApi;
#override
void initState() {
super.initState();
usersApi = UsersApi();
usersApi.fetchAllUsers();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Contacts'),
),
body: Container(
padding: EdgeInsets.all(16),
child:FutureBuilder(
future: itemsApi.fetchAllUsers(),
builder: (BuildContext context, AsyncSnapshot<List<Users>> snapshot)
{
switch(snapshot.connectionState)
{
case ConnectionState.active:
// working
return _loading();
break;
case ConnectionState.waiting:
//working
return _loading();
break;
case ConnectionState.none:
// no connection
return _error('No connection has been made');
break;
case ConnectionState.done:
// completed
if(snapshot.hasError)
{
return _error(snapshot.error.toString());
}
if(snapshot.hasData)
{
return _drawItemsList(snapshot.data,context);
}
break;
}
return Container();
},
),
),
);
}
Widget _drawItemsList(List<Users> myusers,BuildContext context)
{
return ListView.builder(
itemCount: myusers.length,
itemBuilder: (BuildContext context, int position){
return InkWell(
child:Card(
child:Padding(
padding: const EdgeInsets.all(8.0),
child: Text(musers[position].myuser_name),
) ,
),
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context)=>UserDetails(muysers[position].id)));
},
);
},
);
}
Widget _loading()
{
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
}
Widget _error(String error)
{
return Container(
child: Center(
child: Text('Something went wrong'
),
),
);
}
}
my Api call to load listView is here also works fine
class UsersApi {
Future<List<Users>> fetchAllUsers() async //
{
String allUsers = CallApi.url + CallApi.items_url;
Map<String, String> headers =
{
'Accept': 'application/json',
};
var response = await http.get(allUsers, headers: headers);
List<Users> users = []; // its empty list we add it below
if (response.statusCode == 200) {
Map<String, dynamic> body = jsonDecode(response.body);
for (var item in body['data']) {
Users myUsers = Users.fromJson(item);
items.add(myUsers);
}
}
return users;
}
}
Now the error happens remember by passing single id from the first screen makes request of
CallApi.url+CallApi.My_Contacts+itemId;
to second screen and loads details of clicked info.
for loading other information of user from another table i created model and here is my request for second screen
class UserDetailApi {
Future<List<UserDetailModel>> fetchDetailOfSingleUser(String itemId) async
{
String userDetailUrl = CallApi.url+CallApi.My_Contacts+itemId;
Map<String,String> headers =
{
'Accept' : 'application/json',
};
var response = await http.get(userDetailUrl ,headers:headers);
List<UserDetailModel> user =[];
if(response.statusCode == 200)
{
Map<String, dynamic> body = jsonDecode(response.body);
for(var item in body['data'])
{
UserDetailModel userr = UserDetailModel.fromJson(item);
user.add(userr);
}
}
return user;
}
}
here is second activity
class UserDetails extends StatefulWidget
{
final String uId;
UserDetails(this.uId);
#override
_UserDetailsState createState()=>_UserDetailsState();
}
class _UserDetailsState extends State<UserDetails>
{
UserDetailApi itemsApi = UserDetailApi();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(
title:Text('Detailed Info'),
),
body: Container(
padding: EdgeInsets.all(16),
child: FutureBuilder(
future: itemsApi.fetchDetailOfSingleUser(widget.uId),
builder: (BuildContext context, AsyncSnapshot<List<UserDetailModel>> snapshot)
{
switch(snapshot.connectionState)
{
case ConnectionState.active:
// working
return _loading();
break;
case ConnectionState.waiting:
//working
return _loading();
break;
case ConnectionState.none:
// no connection
return _error('No connection has been made');
break;
case ConnectionState.done:
// completed
if(snapshot.hasError)
{
//return _error(snapshot.error.toString());
}
if(snapshot.hasData)
{
return _drawItemsList(snapshot.data);
}
break;
}
return Container();
},
),
),
);
}
Widget _drawItemsList(List<UserDetailModel> mdetail)
{
return ListView.builder(
itemCount: mdetail.length,
itemBuilder: (BuildContext context, int position){
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: mdetail.length,
itemBuilder: (BuildContext context, int position){
return InkWell(
child: Card(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text(here i need to display full user info from second table),
),
),
);
},
),
);
},
);
}
Widget _loading()
{
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
}
Widget _error(String error)
{
return Container(
child: Center(
child: Text('Something went wrong'
),
),
);
}
}
empty screen no results populated on TextBox so please tell me where i have missed or if my works are not correct for detailed activity thanks.

How to hide a button on click in a flutter application?

normal image
image after entering the mobile number
See in this image when a user clicks on the login button the login button should disappear and a text field will be there followed by a submit button.
I have the text field appearing on click of the login button, however I don't know how to disappear that login button once it is pressed.
import 'dart:async';
import 'dart:ffi';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../widgets/landing_page.dart';
import '../screens/register.dart';
import 'package:google_sign_in/google_sign_in.dart';
import '../widgets/google_sign_in_btn.dart';
import '../widgets/reactive_refresh_indicator.dart';
// Each item on AuthStatus represents quite literally the status of the UI.
// On SOCIAL_AUTH only the GoogleSignInButton will be visible.
enum AuthStatus { SOCIAL_AUTH }
class AuthScreen extends StatefulWidget {
#override
_AuthScreenState createState() => _AuthScreenState();
}
// On _AuthScreenState we start by defining the tag that will be used for our logger, then the default status as SOCIAL_AUTH, which means we need to do Google's sign in and the GoogleSignInButton will be visible and interactive.
class _AuthScreenState extends State<AuthScreen> {
String phoneNo;
String smsCode;
String verificationId;
bool _smsCodeDisabled = true;
bool isThere;
bool isPresent;
bool _canShowButton = false;
final db = Firestore.instance;
Firestore.instance.collection('transcriber_user_registeration').where('mobileno', isEqualTo: phoneNo)
// .snapshots().listen(
// (data) { print("Inside phone number check : $data"); });
// // return phoneNumberCheck(phoneNo);
// QuerySnapshot result =
// await Firestore.instance.collection('transcriber_user_registeration').getDocuments();
// var list = result.documents;
// print("Before data loop");
// list.forEach((data) => print(data));
// print("After data loop");
Future<void> phoneNumberCheck(String phoneNo) async {
print("Start of the function");
//bool registerState = false;
//bool isPresent = false;
Firestore.instance
.collection("transcriber_user_registeration")
.getDocuments()
.then((QuerySnapshot snapshot) {
snapshot.documents.forEach((f) async {
if (isPresent = ('${f.data['mobileno']}' == phoneNo)) {
print(isPresent);
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verId) {
this.verificationId = verId;
};
final PhoneCodeSent smsCodeSent =
(String verId, [int forceCodeResend]) {
this.verificationId = verId;
print("im in sms code dialog");
// smsCodeDialog(context).then((value) {
// print('Signed in');
// });
setState(() {
this._smsCodeDisabled = false;
});
};
final PhoneVerificationCompleted verifySuccess =
(AuthCredential user) {
print("verified");
};
final PhoneVerificationFailed verifyFailed =
(AuthException exception) {
print('${exception.message}');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: this.phoneNo,
codeAutoRetrievalTimeout: autoRetrieve,
codeSent: smsCodeSent,
timeout: const Duration(seconds: 5),
verificationCompleted: verifySuccess,
verificationFailed: verifyFailed,
);
}
// else {
// _showMessage();
// }
});
});
//print("End of the function $isPresent");
}
Future<void> verifyPhone() async {
// final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verId) {
// this.verificationId = verId;
// };
var tmp1 = phoneNo.toString();
print('ref stsmt starts $tmp1');
await phoneNumberCheck(phoneNo);
print("After execution of the function $isPresent");
print('bvnnn');
}
Future<bool> smsCodeDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: Text('Enter sms code'),
content: TextField(onChanged: (value) {
this.smsCode = value;
}),
contentPadding: EdgeInsets.all(10.0),
actions: <Widget>[
new FlatButton(
child: Text('Login'),
onPressed: () async {
//await signIn();
await FirebaseAuth.instance.currentUser().then((user) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => LandingPageApp()));
});
})
],
);
});
}
signIn() {
print("came to sign in page");
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
FirebaseAuth.instance.signInWithCredential(credential).then((user) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => LandingPageApp()));
}).catchError((e) {
print(e);
});
}
void hideWidget() {
setState(() {
_canShowButton != _canShowButton;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Container(
color: Colors.white,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Flexible(
flex: 1,
child: new Container(
//color: Colors.white,
height: 200.0,
width: 400.0,
decoration: new BoxDecoration(
image: DecorationImage(
image: new AssetImage('assets/images/download.png'),
),
),
),
),
SizedBox(height: 20.0),
Padding(
padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 20.0),
child: TextFormField(
decoration:
InputDecoration(hintText: 'Enter your Phone number'),
keyboardType: TextInputType.phone,
onChanged: (value) {
this.phoneNo = "+91$value";
},
validator: validateMobile),
),
SizedBox(height: 10.0),
Visibility(
visible: _canShowButton,
child: RaisedButton(
child: Text('Login'),
textColor: Colors.white,
elevation: 7.0,
color: Colors.blue,
onPressed: () async {
bool _canShowButton = true;
await verifyPhone();
setState(() {
_canShowButton = !_canShowButton;
});
hideWidget();
//_number();
},
),
),
_smsCodeDisabled
? SizedBox(height: 10.0)
: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(
horizontal: 20.0, vertical: 16.0),
child: TextFormField(
decoration:
InputDecoration(hintText: 'Enter OTP'),
keyboardType: TextInputType.number,
onChanged: (value) {
this.smsCode = value;
},
validator: validateOtp),
),
SizedBox(height: 10.0),
RaisedButton(
onPressed: () async {
await signIn();
FirebaseAuth.instance.currentUser().then((user) {
print(["user", user]);
if (user != null) {
print(user.uid);
// Navigator.of(context).pop();
//// Navigator.of(context).pushReplacementNamed('/homePage');
// Navigator.of(context).push(
// MaterialPageRoute<Null>(
// builder: (BuildContext context) {
// return new LandingPageApp();
// }));
} else {
print("user is null");
Navigator.of(context).pop();
signIn();
}
});
},
child: Text('Submit'),
textColor: Colors.white,
elevation: 7.0,
color: Colors.blue,
),
],
),
SizedBox(
height: 20.0,
),
Column(
children: <Widget>[
Row(children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(
vertical: 16.0, horizontal: 20.0),
child: Text(
'Not a Registered User?',
style: TextStyle(
fontSize: 16,
),
),
),
MaterialButton(
child: Text(
'Register',
style: TextStyle(
color: Colors.black,
),
),
//color: Colors.blue,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Register()));
},
),
]),
],
),
],
),
));
}
}
String validateMobile(String value) {
//Indian Mobile numbers are of 10 digits only.
if (value.length != 10)
return 'Mobile number must be of 10 digits';
else
return null;
}
String validateOtp(String value) {
//Otp needs to be of 6 digits
if (value.length != 6)
return 'OTP must be of 6 digits';
else
return null;
}
Please try this
bool _canShowButton = true;
void hideWidget() {
setState(() {
_canShowButton = !_canShowButton;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test Screen'),
),
body: Container(
padding: const EdgeInsets.all(8),
child: Column(
children: <Widget>[
Flexible(
flex: 1,
child: Container(
//color: Colors.white,
height: 200.0,
width: 400.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('https://picsum.photos/250?image=10'),
),
),
),
),
SizedBox(height: 20.0),
///if the show button is false
!_canShowButton
? const SizedBox.shrink()
: RaisedButton(
child: Text('Login'),
textColor: Colors.white,
elevation: 7.0,
color: Colors.blue,
onPressed: () {
hideWidget();
//_number();
},
),
],
)),
);
}
In your build method, you already have a boolean _smsCodeDisabled to identify whether the otp field need to show or not. You can use the same boolean to hide the login field and button. The logic should be something like:
#override
Widget build(BuildContext context) {
return _smsCodeDisabled ? _getLoginWidget() : _getOtpWidget();
}
Widget _getLoginWidget() {
// Return Login field and button
}
Widget _getOtpWidget() {
// Return otp field and button
}
Or If you need to only hide the login button, use the following logic:
#override
Widget build(BuildContext context) {
return Scaffold(
// other code
// Login button logic
_smsCodeDisabled ? RaisedButton(...) : Container();
);
}

How can I select and upload Multiple images in Flutter

I tried to find how to select and upload multiple images in flutter but most of the plugins are not working or I did not understand them well. I found little application but it selects and upload only one picture. How to change this code that user can select and upload multiple pictures or is there any other alternatives. Please write in details, i am freshman in coding. Thanks in advance.
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'dart:convert';
import 'package:http_parser/http_parser.dart';
import 'package:toast/toast.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image Upload Demo',
theme: ThemeData(primarySwatch: Colors.pink),
home: ImageInput());
}
}
class ImageInput extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ImageInput();
}
}
class _ImageInput extends State<ImageInput> {
// To store the file provided by the image_picker
File _imageFile;
// To track the file uploading state
bool _isUploading = false;
String baseUrl = 'http://YOUR_IPV4_ADDRESS/flutterdemoapi/api.php';
void _getImage(BuildContext context, ImageSource source) async {
File image = await ImagePicker.pickImage(source: source);
setState(() {
_imageFile = image;
});
// Closes the bottom sheet
Navigator.pop(context);
}
Future<Map<String, dynamic>> _uploadImage(File image) async {
setState(() {
_isUploading = true;
});
// Find the mime type of the selected file by looking at the header bytes of the file
final mimeTypeData =
lookupMimeType(image.path, headerBytes: [0xFF, 0xD8]).split('/');
// Intilize the multipart request
final imageUploadRequest =
http.MultipartRequest('POST', Uri.parse(baseUrl));
// Attach the file in the request
final file = await http.MultipartFile.fromPath('image', image.path,
contentType: MediaType(mimeTypeData[0], mimeTypeData[1]));
// Explicitly pass the extension of the image with request body
// Since image_picker has some bugs due which it mixes up
// image extension with file name like this filenamejpge
// Which creates some problem at the server side to manage
// or verify the file extension
imageUploadRequest.fields['ext'] = mimeTypeData[1];
imageUploadRequest.files.add(file);
try {
final streamedResponse = await imageUploadRequest.send();
final response = await http.Response.fromStream(streamedResponse);
if (response.statusCode != 200) {
return null;
}
final Map<String, dynamic> responseData = json.decode(response.body);
_resetState();
return responseData;
} catch (e) {
print(e);
return null;
}
}
void _startUploading() async {
final Map<String, dynamic> response = await _uploadImage(_imageFile);
print(response);
// Check if any error occured
if (response == null || response.containsKey("error")) {
Toast.show("Image Upload Failed!!!", context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
} else {
Toast.show("Image Uploaded Successfully!!!", context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
}
}
void _resetState() {
setState(() {
_isUploading = false;
_imageFile = null;
});
}
void _openImagePickerModal(BuildContext context) {
final flatButtonColor = Theme.of(context).primaryColor;
print('Image Picker Modal Called');
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 150.0,
padding: EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
Text(
'Pick an image',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(
height: 10.0,
),
FlatButton(
textColor: flatButtonColor,
child: Text('Use Camera'),
onPressed: () {
_getImage(context, ImageSource.camera);
},
),
FlatButton(
textColor: flatButtonColor,
child: Text('Use Gallery'),
onPressed: () {
_getImage(context, ImageSource.gallery);
},
),
],
),
);
});
}
Widget _buildUploadBtn() {
Widget btnWidget = Container();
if (_isUploading) {
// File is being uploaded then show a progress indicator
btnWidget = Container(
margin: EdgeInsets.only(top: 10.0),
child: CircularProgressIndicator());
} else if (!_isUploading && _imageFile != null) {
// If image is picked by the user then show a upload btn
btnWidget = Container(
margin: EdgeInsets.only(top: 10.0),
child: RaisedButton(
child: Text('Upload'),
onPressed: () {
_startUploading();
},
color: Colors.pinkAccent,
textColor: Colors.white,
),
);
}
return btnWidget;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Upload Demo'),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 40.0, left: 10.0, right: 10.0),
child: OutlineButton(
onPressed: () => _openImagePickerModal(context),
borderSide:
BorderSide(color: Theme.of(context).accentColor, width: 1.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.camera_alt),
SizedBox(
width: 5.0,
),
Text('Add Image'),
],
),
),
),
_imageFile == null
? Text('Please pick an image')
: Image.file(
_imageFile,
fit: BoxFit.cover,
height: 300.0,
alignment: Alignment.topCenter,
width: MediaQuery.of(context).size.width,
),
_buildUploadBtn(),
],
),
);
}
}
I used that package:
dependencies:
multi_image_picker: ^4.6.7
Ex:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:multi_image_picker/multi_image_picker.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Asset> images = List<Asset>();
String _error;
#override
void initState() {
super.initState();
}
Widget buildGridView() {
if (images != null)
return GridView.count(
crossAxisCount: 3,
children: List.generate(images.length, (index) {
Asset asset = images[index];
return AssetThumb(
asset: asset,
width: 300,
height: 300,
);
}),
);
else
return Container(color: Colors.white);
}
Future<void> loadAssets() async {
setState(() {
images = List<Asset>();
});
List<Asset> resultList;
String error;
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 300,
);
} on Exception catch (e) {
error = e.toString();
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
images = resultList;
if (error == null) _error = 'No Error Dectected';
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
),
body: Column(
children: <Widget>[
Center(child: Text('Error: $_error')),
RaisedButton(
child: Text("Pick images"),
onPressed: loadAssets,
),
Expanded(
child: buildGridView(),
)
],
),
),
);
}
}
Add dependecy of image_picker:
image_picker: ^0.8.4+3
Then make a method for selectImages():
final ImagePicker imagePicker = ImagePicker();
List<XFile>? imageFileList = [];
void selectImages() async {
final List<XFile>? selectedImages = await
imagePicker.pickMultiImage();
if (selectedImages!.isNotEmpty) {
imageFileList!.addAll(selectedImages);
}
print("Image List Length:" + imageFileList!.length.toString());
setState((){});
}
Create a builder for showing selected Images:
return Scaffold(
appBar: AppBar(
title: Text('Multiple Images'),
),
body: SafeArea(
child: Column(
children: [
ElevatedButton(
onPressed: () {
selectImages();
},
child: Text('Select Images'),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
itemCount: imageFileList!.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return Image.file(File(imageFileList![index].path),
fit: BoxFit.cover,);
}),
),
),
],
),
));
Complete Source code available in github link...
https://github.com/NishaJain24/multi_image_picker

How can I login on Flutter app with Laravel basic authenticaiton using email and password

After creating a new Laravel project, what I did was only making authentication using php artisan make:auth. I needed to get CSRF token to log in on the Flutter app, so I simple commented out CSRF token in the middleware.
The error I am getting is 302 which is redirection error. I was redirected to '/home'. So, I just simply returned 'User data' when successfully logged in, but I am still getting the same error. There is not enough documentation about Flutter of how to authenticate using email and password if i have web service already deployed on the web hosting company.
If I installed basic authentication on Laravel, how can I authenticate on Flutter? I don't need CSRF token for now for testing purposes.
flutter login code
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:embedded_mobile/screens/home.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:embedded_mobile/models/user.dart';
var routes = <String, WidgetBuilder>{
"/home": (BuildContext context) => HomeScreen(),
"/login": (BuildContext context) => LoginWithRestfulApi(),
};
class LoginWithRestfulApi extends StatefulWidget {
#override
_LoginWithRestfulApiState createState() => _LoginWithRestfulApiState();
}
class _LoginWithRestfulApiState extends State<LoginWithRestfulApi>
with TickerProviderStateMixin {
static var uri = "http://blackboardembedded.dx.am";
static BaseOptions options = BaseOptions(
baseUrl: uri,
responseType: ResponseType.plain,
connectTimeout: 30000,
receiveTimeout: 30000,
followRedirects: false,
validateStatus: (code) {
return code < 500;
});
static Dio dio = Dio(options);
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
TextEditingController _emailController = TextEditingController();
TextEditingController _passwordController = TextEditingController();
bool _isLoading = false;
Future<bool> _onBackPressed() {}
Future<dynamic> _loginUser(String email, String password) async {
try {
Options options = Options(
contentType: ContentType.parse('application/json'),
);
Response response = await dio.post('/login',
data: {"email": email, "password": password}, options: options);
if (response.statusCode == 200 ||
response.statusCode == 201 ) {
var responseJson = json.decode(response.data);
return responseJson;
} else if (response.statusCode == 401) {
throw Exception("Incorrect Email/Password");
} else
throw Exception('Authentication Error');
} on DioError catch (exception) {
if (exception == null ||
exception.toString().contains('SocketException')) {
throw Exception("Network Error");
} else if (exception.type == DioErrorType.RECEIVE_TIMEOUT ||
exception.type == DioErrorType.CONNECT_TIMEOUT) {
throw Exception(
"Could'nt connect, please ensure you have a stable network.");
} else {
return null;
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
leading: new IconButton(
icon: new Icon(Icons.arrow_back, color: Colors.black),
onPressed: () => Navigator.of(context).pushNamed('/login'),
),
title: Text("Send Location")),
body: Center(
child: Container(
child: _isLoading
? CircularProgressIndicator()
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20.0),
child: TextField(
controller: _emailController,
decoration: InputDecoration(
hintText: 'Email',
),
),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: TextField(
controller: _passwordController,
decoration: InputDecoration(
hintText: 'Password',
),
),
),
new SizedBox(
width: 200,
height: 50,
child: RaisedButton(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(50.0)),
child: Text("Login"),
color: Colors.amber,
onPressed: () async {
setState(() => _isLoading = true);
var res = await _loginUser(
_emailController.text, _passwordController.text);
setState(() => _isLoading = false);
JsonUser user = JsonUser.fromJson(res);
if (user != null) {
Navigator.of(context).push(MaterialPageRoute<Null>(
builder: (BuildContext context) {
return new LoginScreen(
user: user,
);
}));
} else {
Scaffold.of(context).showSnackBar(SnackBar(
content:
Text("Email or password is incorrect")));
}
},
),
),
],
),
),
),
);
}
}
class LoginScreen extends StatelessWidget {
LoginScreen({#required this.user});
final JsonUser user;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Login Screen")),
body: Center(
child: user != null
? Text("Logged IN \n \n Email: ${user.email} ")
: Text("Yore not Logged IN"),
),
);
}
}
for your app, you need to authenticate users over the api routes, which does not require CSRF tokens by default. you can achieve this using Laravel Passport or JWT auth

Resources