Flutter Deleting a record through the API - laravel

I am using a Laravel Api and a Flutter app as the client. This is a basic CRUD app and i was able to update the records through the API but when it comes to deleting a record, an exception is thrown and i am unable to findout the issue.
Laravel 8 App looks like this
routes\api.php
Route::apiResource('categories', \App\Http\Controllers\Api\CategoryController::class);
app\Http\Controllers\Api\CategoryController.php
public function destroy(Category $category)
{
$category->delete();
return response()->noContent();
}
Flutter App looks like this
lib\screens\categories.dart
import 'package:flutter/material.dart';
import 'package:demo_app/models/category.dart';
import 'package:demo_app/widgets/category_edit.dart';
import 'package:demo_app/providers/category_provider.dart';
import 'package:provider/provider.dart';
// import 'package:http/http.dart' as http;
class Categories extends StatefulWidget {
const Categories({super.key});
#override
CategoriesState createState() => CategoriesState();
}
class CategoriesState extends State<Categories> {
#override
Widget build(BuildContext context) {
final provider = Provider.of<CategoryProvider>(context);
List<Category> categories = provider.categories;
return Scaffold(
appBar: AppBar(
title: const Text('Categories'),
),
body: ListView.builder(
itemCount: categories.length,
itemBuilder: (context, index) {
Category category = categories[index];
return ListTile(
title: Text(category.name),
trailing:
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) {
return CategoryEdit(category, provider.updateCategory);
});
},
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () => showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('confirmation'),
content: const Text('Are you sure you want to delete?'),
actions: [
TextButton(
onPressed: () => deleteCategory(provider.deleteCategory, category),
child: const Text('Confirm')
),
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel')
),
]
);
}
),
),
],
),
);
},
)
);
}
Future deleteCategory(Function callback, Category category) async{
await callback(category);
if (!mounted) return;
Navigator.pop(context);
}
}
lib\services\api.dart
// import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:demo_app/models/category.dart';
// import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class ApiService{
ApiService();
final String baseUrl = 'http://flutter-api.test/api/';
Future<List<Category>> fetchCategories() async {
http.Response response =
await http.get(Uri.parse('${baseUrl}categories'));
List categories = jsonDecode(response.body);
return categories.map((category) => Category.fromJson(category)).toList();
}
Future <Category> updateCategory(Category category) async {
// String uri = '${baseUrl}categories/$category.id';
String uri = '${baseUrl}categories/${category.id}';
http.Response response = await http.put(Uri.parse(uri),
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
HttpHeaders.acceptHeader: 'application/json',
},
body: jsonEncode({'name': category.name}));
if(response.statusCode != 200){
throw Exception('Error happened on update');
}
return Category.fromJson(jsonDecode(response.body));
}
Future<void> deleteCategory(id) async {
String uri = '${baseUrl}categories/$id';
http.Response response = await http.delete(Uri.parse(uri));
if(response.statusCode != 204){
throw Exception('Error happened on delete');
}
}
}
lib\providers\category_provider.dart
// import 'dart:io';
import 'package:demo_app/models/category.dart';
import 'package:flutter/material.dart';
import 'package:demo_app/services/api.dart';
class CategoryEdit extends StatefulWidget {
final Category category;
final Function categoryCallback;
const CategoryEdit(this.category, this.categoryCallback, {Key? key}) : super(key: key);
#override
CategoryEditState createState() => CategoryEditState();
}
class CategoryEditState extends State<CategoryEdit> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final categoryNameController = TextEditingController();
ApiService apiService = ApiService();
String errorMessage = '';
#override
void initState(){
categoryNameController.text = widget.category.name;
super.initState();
}
#override
Widget build(BuildContext context){
return Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: Column(children: <Widget>[
TextFormField(
onChanged: (text) => setState(() => errorMessage = ''),
controller: categoryNameController,
validator: (String? value) {
if (value!.isEmpty) {
return 'Enter category name';
}
return null;
},
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Category Name',
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
ElevatedButton(
child: const Text('Save'),
onPressed: () => saveCategory(context),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red
),
child: const Text('Cancel'),
onPressed: () => Navigator.pop(context),
),
],
),
Text(errorMessage, style: const TextStyle(color: Colors.red),)
]
)
)
);
}
Future saveCategory(BuildContext context) async {
final form = _formKey.currentState;
if(!form!.validate())
{
return;
}
widget.category.name = categoryNameController.text;
await widget.categoryCallback(widget.category);
if (!mounted) return;
Navigator.pop(context);
}
}
This is the exception i get
flutter: Exception: Error happened on delete

It returns the Error Code 500 and i was able to sort the error from the Server side

Related

MissingPluginException(No implementation found for method login on channel app.meedu/flutter_facebook_auth)

i got this error but not solve.i follow the step properly
this is code of my login page
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:provider/provider.dart';
class loginpage extends StatefulWidget {
const loginpage({Key? key}) : super(key: key);
#override
State<loginpage> createState() => _loginpageState();
}
enum LoginType { facebook, google, apple }
class _loginpageState extends State<loginpage> {
FirebaseAuth _auth = FirebaseAuth.instance;
bool _fetching = true;
User? get _currentUser => _auth.currentUser;
#override
void initState() {
super.initState();
_checkLogin();
}
Future<void> _checkLogin() async {
await Future.delayed(Duration(seconds: 1));
_fetching = false;
setState(() {});
}
void _login(LoginType type) async {
setState(() {
_fetching = true;
});
OAuthCredential? credential;
if (type == LoginType.facebook) {
credential = (await _loginWithFacebook()) as OAuthCredential;
} else if (type == LoginType.google) {
credential = (await _loginWithGoogle()) as OAuthCredential;
}
if (credential != null) {
await _auth.signInWithCredential(credential);
}
setState(() {
_fetching = false;
});
}
Future<OAuthCredential?> _loginWithFacebook() async {
final AccessToken accessToken = (await FacebookAuth.instance.login()) as AccessToken;
print(accessToken);
return FacebookAuthProvider.credential(
accessToken.token,
);
}
Future<OAuthCredential?> _loginWithGoogle() async {
try {
final GoogleSignInAccount? account = await GoogleSignIn(scopes: ['email']).signIn();
if (account != null) {
// Obtain the auth details from the request
final GoogleSignInAuthentication googleAuth = await account.authentication;
return GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
}
return null;
} catch (e) {
print(e);
return null;
}
}
Future<void> _logOut() async {
final List<UserInfo> data = _currentUser!.providerData;
String providerId = "firebase";
for (final item in data) {
if (item.providerId != "firebase") {
providerId = item.providerId;
break;
}
}
await _auth.signOut();
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (!_fetching && _currentUser == null)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () => _login(LoginType.facebook),
child: Text("FACEBOOK"),
),
SizedBox(width: 10),
TextButton (
onPressed: () => _login(LoginType.google),
child: Text("GOOGLE"),
),
],
),
if (_fetching) CircularProgressIndicator(),
if (_currentUser != null) ...[
Text("HI ...."),
Text(_currentUser!.displayName ??" "),
SizedBox(height: 20),
TextButton(
onPressed: _logOut,
child: Text("LOG OUT"),
),
]
],
),
),
),
);
}
}
how to solve this? i follow all instruction properly.

Hero animation with gallery flicker Flutter

I'm trying to create a basic Hero animation between a gallery grid view page (using photo manager plugin) and a detail page. When the hero animation is done both back and forward, the picture is flickering .
Here is the example I've made :
The full code runnable :
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:photo_manager/photo_manager.dart';
void main() {
runApp(const TestApp());
}
class TestApp extends StatefulWidget {
const TestApp({Key? key}) : super(key: key);
#override
State<TestApp> createState() => _TestAppState();
}
class _TestAppState extends State<TestApp> {
final List<AssetEntity> assetsList = [];
bool granted = false;
void loadAssets() async {
granted = await PhotoManager.requestPermission();
if (granted) {
FilterOptionGroup option = FilterOptionGroup()
..addOrderOption(const OrderOption(
type: OrderOptionType.createDate,
asc: false,
));
final albums = await PhotoManager.getAssetPathList(filterOption: option, type: RequestType.image);
print("albums : $albums");
if (albums.isNotEmpty) {
var alb = albums.where((element) {
return element.name == 'Test';
});
var album = alb.first;
// Now that we got the album, fetch all the assets it contains
List<AssetEntity> currentList =
await album.getAssetListRange(start: 0, end: 200);
// Update the state and notify UI
assetsList.clear();
assetsList.addAll(currentList);
}
setState(() {});
}
}
#override
void initState() {
loadAssets();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: granted
? _gridView()
: Center(
child: Container(
color: Colors.blue,
width: 200,
height: 200,
child: TextButton(
onPressed: () async {
granted = await PhotoManager.requestPermission();
setState(() {});
},
child: const Text(
"Ask permission",
style: TextStyle(color: Colors.white),
),
)),
)),
);
}
Widget _gridView() {
return GridView.builder(
itemCount: assetsList.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (context, index) {
return Hero(
createRectTween: (Rect? begin, Rect? end) {
RectTween _rectTween = RectTween(begin: begin, end: end);
return _rectTween;
},
tag: assetsList[index].id,
child: GalleryThumbnail(
asset: assetsList[index],
onTap: (bytes) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return DetailsPage(asset: assetsList[index], bytes: bytes);
}));
},
));
});
}
}
class GalleryThumbnail extends StatelessWidget {
final AssetEntity asset;
final ValueChanged<Uint8List> onTap;
const GalleryThumbnail({Key? key, required this.asset, required this.onTap})
: super(key: key);
#override
Widget build(BuildContext context) {
return FutureBuilder<Uint8List?>(
future: Platform.isIOS
? asset.thumbDataWithOption(
ThumbOption.ios(
width: 500,
height: 500,
deliveryMode: DeliveryMode.opportunistic,
resizeMode: ResizeMode.fast,
resizeContentMode: ResizeContentMode.fit,
quality: 100
// resizeContentMode: ResizeContentMode.fill,
),
)
: asset.thumbDataWithSize(250, 250),
builder: (_, snapshot) {
final bytes = snapshot.data;
if (snapshot.hasError) {
return Container();
}
// If we have no data
if (bytes == null) return Container();
// If there's data, display it as an image
return GestureDetector(
onTap: () {
onTap(bytes);
},
child: Image.memory(bytes, fit: BoxFit.cover,gaplessPlayback: true,));
},
);
}
}
class DetailsPage extends StatefulWidget {
final AssetEntity asset;
final Uint8List bytes;
const DetailsPage({Key? key, required this.asset, required this.bytes})
: super(key: key);
#override
_DetailsPageState createState() => _DetailsPageState();
}
class _DetailsPageState extends State<DetailsPage> {
late ImageProvider _imageProvider;
Future<void> loadFile() async {
try {
File? file = await widget.asset.file;
if (file == null) return;
_imageProvider = Image.file(file).image;
setState(() {});
} catch (e) {
print("error to load file : " + e.toString());
}
}
#override
void initState() {
_imageProvider = Image.memory(widget.bytes).image;
loadFile();
super.initState();
}
#override
Widget build(BuildContext context) {
double ratio = widget.asset.height / widget.asset.width;
return Scaffold(
body: Stack(
children: [
Hero(
createRectTween: (Rect? begin, Rect? end) {
RectTween _rectTween = RectTween(begin: begin, end: end);
return _rectTween;
},
tag: widget.asset.id,
child: Center(
child: Image(image: _imageProvider,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height*ratio,
gaplessPlayback: true),
),
),
const SafeArea(
child: Padding(
padding: EdgeInsets.all(8.0),
child: BackButton(),
)),
],
),
);
}
}
I've implemented hero animation somewhere else in my project and I'm not getting this behavior where as here I do.
Why this is happening and how to correct this ?
Add
transitionOnUserGestures: true,
to Hero() widget in current & next page
This happens because the Hero of the image thumbail wraps the FutureBuilder, and then the future triggers again during the animation, wraps the Image instead of the FutureBuilder.

How to Display Picked Image in Flutter

I want to Pick a image and then display the picked Image. So I tried doing this->
class ImageUploadChoice extends StatefulWidget {
#override
_ImageUploadChoiceState createState() => _ImageUploadChoiceState();
}
class _ImageUploadChoiceState extends State<ImageUploadChoice> {
File imageFile;
Future getImage(int type) async {
PickedFile pickedImage = await ImagePicker().getImage(
source: type == 1 ? ImageSource.camera : ImageSource.gallery,
imageQuality: 50
);
return pickedImage;
}
For Displaying Image
Expanded(
child: Container(
child: imageFile != null
? Image.file(
imageFile,
height: MediaQuery.of(context).size.height / 5,
)
: Text("Pick up the image"),
),
),
For Calling Function->
new ListTile(
leading: new Icon(
Icons.photo_library,
color: Colors.black,),
title: new Text(
'Photo Library',
style: getTextStyle(MediaQuery.of(context).size.height, "heading2"),
),
onTap: () async {
final tmpFile = await getImage(2);
setState(() {
imageFile = tmpFile;
});
Navigator.pop(context);
Navigator.of(context).pop();
}),
But It's not Working. The Selected Image is not been displayed. What else I need to do?
Made a sample code and it's working fine. Remove following lines
Navigator.pop(context);
Navigator.of(context).pop();
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Test(),
);
}
}
class Test extends StatefulWidget {
#override
_Test createState() => _Test();
}
class _Test extends State<Test> {
File imageFile;
Future getImage() async {
final pickedFile = await ImagePicker().getImage(source: ImageSource.gallery);
setState(() {
if (pickedFile != null) {
imageFile = File(pickedFile.path);
} else {
print('No image selected.');
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: InkWell(
onTap: (){
getImage();
},
child: Icon(
Icons.add
),
),
body: Center(
child: root(),
),
);
}
Widget root() {
return Container(
child: imageFile != null
? Image.file(
imageFile,
height: MediaQuery
.of(context)
.size
.height / 5,
)
: Text("Pick up the image"),
);
}
}

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