How to perform signOut on sidenavbar using switch statement in flutter? - flutter

I am new to flutter and I am trying to perform signOut() function on sidenavbar. I got stuck in a code segment. After a couple of attempts, I got errors like:
type Future is not a subtype of type Widget
I have the following code. How should I call _logOutUser() function in my case 0: statement?
Any help would be highly appreciated
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
routes: {
'/login': (context) => Login(),
},
title: 'NavigationDrawer Demo',
theme: new ThemeData(
primarySwatch: Colors.red,
),
home: new HomePage(),
);
}
}
class DrawerItem {
String title;
IconData icon;
DrawerItem(this.title, this.icon);
}
class HomePage extends StatefulWidget {
HomePage({
this.auth,
this.onSignedOut,
});
final AuthImplementation auth;
final VoidCallback onSignedOut;
final drawerItems = [
new DrawerItem("Logout", Icons.exit_to_app),
];
#override
State<StatefulWidget> createState() {
return new HomePageState();
}
}
class HomePageState extends State<HomePage> {
void _logOutUser() async {
try {
await widget.auth.signOut();
widget.onSignedOut();
} catch (e) {
print(e.toString());
}
}
int _selectedDrawerIndex = 0;
_getDrawerItemWidget(int pos) {
switch (pos) {
case 0:
return HomePageState()._logOutUser();
default:
return new Text("Error");
}
}
_onSelectItem(int index) {
setState(() => _selectedDrawerIndex = index);
Navigator.of(context).pop();
}
#override
Widget build(BuildContext context) {
var drawerOptions = <Widget>[];
for (var i = 0; i < widget.drawerItems.length; i++) {
var d = widget.drawerItems[i];
drawerOptions.add(
new ListTile(
leading: new Icon(d.icon),
title: new Text(d.title),
selected: i == _selectedDrawerIndex,
onTap: () => _onSelectItem(i),
)
);
}
}
}

I'm a little confused by your question, but if I'm not mistaken why don't you just do this:
switch (pos) {
case 0:
HomePageState()._logOutUser();
return Text("Logged Out");
// or if you don't want to show anything you could just return a blank Container, or
// if you wanted to navigate to login screen just use Navigator.push() and return the Container
default:
return new Text("Error");
}
The reason you are getting the error type Future<dynamic> is not a subtype of Widget is because your Function _logOutUser() isn't returning a Widget. Comment below if I didn't answer your question.

Related

Stream Builder with HTTP request is not working [Flutter]

I tried for the first time using Stream builder with HTTP requests. it is not updating data.
find below my code. Thanks for helping me!
///////////////////////////////////////// ////////////////////////////////// /////////////// //////////////////// ///////////////////// //////////////////// ///////////////////////////
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner:false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({ Key? key }) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: listMarkers,
builder: (BuildContext context,AsyncSnapshot<List<Coordonnees>> snapshot){
switch (snapshot.connectionState){
case ConnectionState.none:
// TODO: Handle this case.
// break;
case ConnectionState.waiting:
// TODO: Handle this case.
// break;
case ConnectionState.active:
return Center(child:CircularProgressIndicator());
// break;
case ConnectionState.done:
List<Coordonnees>? mark=snapshot.data;
return Center(
child:Text('latitude: ${mark![0].latitude}',
style: TextStyle(
fontSize: 30,
)
),
);
// break;
}
},
),
);
}
List<Coordonnees> marqueurs=[];
Future<List<Coordonnees>> UpdateMarkers(String id) async{
Map data = {
'idligne': id,
};
final response = await http.post(
Uri.parse("https://www.sirius-iot.eu/Dev/ESI/qitari2021/sarah.php?lignegps"),
body: data);
Map<String,dynamic> map=json.decode(response.body);
List<dynamic> dataa = map['lignegps'];
marqueurs.clear();
for(var i in dataa)
{
Coordonnees crd=Coordonnees(double.parse(i['latitude']),double.parse(i['longitude']),double.parse(i['vitesse']));
marqueurs.add(crd);
}
return marqueurs;
}
Stream<List<Coordonnees>> get listMarkers async*{
String id='1';
yield await UpdateMarkers(id) ;
}
}
class Coordonnees{
double latitude;
double longitude;
double vitesse;
Coordonnees(this.latitude,this.longitude,this.vitesse);
}
You can try .asStream() instead of manually yielding data which is awaited.
void main() {
listMarkers.listen((coordinates) {
print(coordinates);
});
}
class Coordinates {
int x;
int y;
Coordinates(this.x, this.y);
}
Future<List<Coordinates>> updateMarkers(String id) async{
await Future.delayed(Duration(seconds: 2));
return [
Coordinates(1,2)
];
}
Stream<List<Coordinates>> get listMarkers => updateMarkers('1').asStream();
Notice how elegant Stream<List<Coordinates>> get listMarkers => updateMarkers('1').asStream(); is as compared to what you are doing. And hopefully it will solve your issue too.

How to dynamically update a particular list view item while using a ListView.builder?

I am making a list view in Flutter. I want to update an item's property when the item is long pressed.
Following is the complete Code:
// main.dart
import 'package:LearnFlutter/MyList.dart';
import 'package:flutter/material.dart';
import 'MyList.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'List Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'My list demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: MyList(),
);
}
}
// MyList.dart
import 'package:flutter/material.dart';
class Item {
Item(String name, bool selected, Color color) {
_name = name;
_selected = selected;
_color = color;
}
String _name;
bool _selected;
Color _color;
String getName() {
return _name;
}
bool isSelected() {
return _selected;
}
void toggleSelected() {
_selected = !_selected;
}
void setColor(Color color) {
_color = color;
}
Color getColor() {
return _color;
}
}
class MyList extends StatefulWidget {
#override
_CardyState createState() => new _CardyState();
}
class _CardyState extends State<MyList> {
#override
Widget build(BuildContext context) {
var itemsList = [
Item('My item1', false, Colors.grey[200]),
Item('My item2', false, Colors.grey[200]),
Item('My item3', false, Colors.grey[200]),
];
return ListView.builder(
itemCount: itemsList.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Icon(Icons.train),
title: Text(itemsList[index].getName()),
trailing: Icon(Icons.keyboard_arrow_right),
tileColor: itemsList[index].getColor(),
selected: itemsList[index].isSelected(),
onLongPress: () {
toggleSelection(itemsList[index]);
},
),
);
},
);
}
void toggleSelection(Item item) {
print(item.getName() + ' long pressed');
setState(() {
item.toggleSelected();
if (item.isSelected()) {
item.setColor(Colors.blue[200]);
} else {
item.setColor(Colors.grey[200]);
}
});
}
}
Question:
In the above code toggleSelection is getting called on long press event. But the item's color does not get updated. What am I doing wrong?
The main reason it is not functioning properly is that you have no state in your class Item, so you are not re-building/updating anything. If you would like to handle it there in the class, then you will need to extend it to the ChangeNotifier. You will also need to use the ChangeNotifierProvider, look at the docs for help: https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
You will need the provider package: https://pub.dev/packages/provider
Class Item
class Item extends ChangeNotifier {
Item(String name, Color color) {
_name = name;
_color = color;
}
int selectedIndex; // to know active index
String _name;
Color _color;
String getName() {
return _name;
}
void toggleSelected(int index) {
selectedIndex = index;
notifyListeners(); // To rebuild the Widget
}
void setColor(Color color) {
_color = color;
notifyListeners();
}
Color getColor() {
return _color;
}
}
Widget List
class MyList extends StatefulWidget {
#override
_CardyState createState() => new _CardyState();
}
class _CardyState extends State<MyList> {
#override
Widget build(BuildContext context) {
final items = Provider.of<Item>(context); // Accessing the provider
bool selected = false; // default val. of bool
var itemsList = [
Item('My item1', Colors.grey[200]),
Item('My item2', Colors.grey[200]),
Item('My item3', Colors.grey[200]),
];
return ListView.builder(
itemCount: itemsList.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Icon(Icons.train),
title: Text(itemsList[index].getName()),
trailing: Icon(Icons.keyboard_arrow_right),
tileColor: items.selectedIndex == index
? items.getColor()
: Colors.grey[200],
selected: items.selectedIndex == index ? true : false,
onLongPress: () {
setState(() => selected = !selected);
items.toggleSelected(index);
if (selected) {
items.setColor(Colors.red);
}
},
),
);
},
);
}
}
make MyList into a stateless widget keep all the data that it should show in the HomePage which is a statefull widget including the data about the selected items. then pass the data into MyList
here is how your MyList could be
class MyList extends StatelessWidget {
final List<Item> items;
final List<int> selectedItemIdList;
final void Function(Item) onLongClick;
MyList(this.items, this.selectedItemIdList, this.onLongClick);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, position) {
//remember all you need to do here is to create your item based on the data you have
var item = items[position];
var isSelected = items.firstWhere((element) => item.id == element.id) != null;
if (isSelected) {
//build and return a widget with selected look
} else {
return GestureDetector(
onLongPress: () => onLongClick(item), //changes data in homepage then MyList will be updated automatically
child: Container(
//rest of your widget
),
);
}
},
itemCount: items.length,
);
}
}
inside your HomePageState
//all the data the list it build from should be stored here not inside the list. and
List<Item> items = [ ... ];
List<int> selectedItemIdList = [ ... ];
//MyList is just a Stateless widget that only shows this data change (a very DUMB view as one could say)
#override
Widget build(BuildContext context) {
return MyList(items, selectedItemIdList, (item) {
setState((){
selectedItemIdList.add(item.id);
});
});
}

Can't find the stream provider when using named route navigation

I have a Stream Provider (connected to firebase) that is not working. I am guessing that the problem lies in the fact that I am using a named navigator [Navigator.pushNamed(context, '/route',)]. I guess this makes the 'route' widget to not be the son of the widget that calls it. Let me show it better below.
My app structure is as follows:
My main widget which handles routing and receives the Stream with user authentication (there is no problem here):
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: AuthService().user,
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Wrapper(),
routes: {
'/home': (context) => Wrapper(),
'/edit_profile': (context) => UserProfile() //This is where I am having trouble.
}
),
);
}
}
The Wrapper that validates if the user is authenticated and acts accordingly:
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
// return either the Home or Authenticate widget
if (user == null){
return Authenticate();
} else {
return HomeWrapper();
}
}
}
The HomeWrapper which receives the second stream and redirects to the widget I am having trouble with:
class HomeWrapper extends StatefulWidget {
#override
_HomeWrapperState createState() => _HomeWrapperState();
}
class _HomeWrapperState extends State<HomeWrapper> {
String currentBodyName = 'home';
Widget currentBodyWidget = Home();
#override
Widget build(BuildContext context) {
Widget _drawerOptions = Row(
children: [
FlatButton(child: someChild, onPressed: () {Navigator.pushNamed(context, '/edit_profile',);},), //This is the actual call to the navigator.
],
);
return StreamProvider<Map>.value( //This is the problematic Stream!
value: DatabaseService().userDetail,
child: Scaffold(
//Body
body: currentBodyWidget,
//I am simplifying this to show the most important parts
bottomNavigationBar: myBottomNavigationBar(
buttons: <Widget>[
FlatButton(
icon: someIcon,
onPressed: () => _onItemTapped('home'),
),
FlatButton(
icon: otherIcon,
onPressed: () => _onItemTapped('second_screen'),
),
],)
//Drawer
drawer: Drawer(child: _drawerOptions,), //This one has the call to the problematic edit_profile route.
);
}
void _onItemTapped(String newBodyName) {
if (newBodyName != currentBodyName){
setState(() {
currentBodyName = newBodyName;
switch(newBodyName) {
case 'home': {
currentBodyWidget = Home();
}
break;
case 'second_screen': {
currentBodyWidget = SecondScreen();
}
break;
default: {
currentBodyWidget = Home();
}
break;
}
});
}
}
}
Finally the edit_profile route calls the UserProfile Widget which looks like this:
class UserProfile extends StatefulWidget {
#override
_UserProfileState createState() => _UserProfileState();
}
class _UserProfileState extends State<UserProfile> {
#override
Widget build(BuildContext context) {
//This is where the error occurs!!
final userDocument = Provider.of<Map>(context) ?? [];
print(userDocument);
return Scaffold(body: Container());
}
}
This is the error that it throws:
The following ProviderNotFoundError was thrown building UserProfile(dirty, state: _UserProfileState#09125):
Error: Could not find the correct Provider<Map<dynamic, dynamic>> above this UserProfile Widget
Thank you very much!!
Turns out my approach was wrong.
Instead of wrapping the HomeWrapper with the StreamProvider, hoping that it would pass the data to the next route (UserProfile ), what I did was to wrap the UserProfile widget with a StreamProvider, as follows:
(Note: I changed the Map StreamProvider for a UserData StreamProvider.)
class UserProfile extends StatefulWidget {
#override
_UserProfileState createState() => _UserProfileState();
}
class _UserProfileState extends State<UserProfile> {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
return StreamBuilder<UserData>(
stream: DatabaseService(uid: user.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
return Scaffold(
body: Container(
//My Widget here
);
} else
return Loading();
});
}
}
This series was very helpful: https://www.youtube.com/playlist?list=PL4cUxeGkcC9j--TKIdkb3ISfRbJeJYQwC

Implementing Google Vision API with Flutter

I'm unsure of how to incorporate this into an existing flutter project and I haven't been able to find any useful guides or tips online. Im looking to implement a 2D only barcode scanner, and none of the existing flutter barcode scanners have that option.
I know ZXing also has the 2d only functionality in it so I could be swayed to use that if anyone can point me in the direction of how to implement them in flutter
Please check this url
https://pub.dartlang.org/packages/qrcode_reader
Here is implementation code
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:qrcode_reader/QRCodeReader.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'QRCode Reader Demo',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
final Map<String, dynamic> pluginParameters = {
};
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<String> _barcodeString;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text('QRCode Reader Example'),
),
body: new Center(
child: new FutureBuilder<String>(
future: _barcodeString,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
return new Text(snapshot.data != null ? snapshot.data : '');
})),
floatingActionButton: new FloatingActionButton(
onPressed: () {
setState(() {
_barcodeString = new QRCodeReader()
.setAutoFocusIntervalInMs(200)
.setForceAutoFocus(true)
.setTorchEnabled(true)
.setHandlePermissions(true)
.setExecuteAfterPermissionGranted(true)
.scan();
});
},
tooltip: 'Reader the QRCode',
child: new Icon(Icons.add_a_photo),
),
);
}
}
This can be done by using flutter barcode_scan dependency.
Future _openQRScanner() async {
try {
// Below code will open camera preview and return result after qr scan
String _content = await BarcodeScanner.scan();
setState(() => this._content = _content);
} on PlatformException catch (e) {
if (e.code == BarcodeScanner.CameraAccessDenied) {
showSnackBar('Please grant camera permission!');
setState(() {
this._content = null;
});
} else {
showSnackBar('Error: $e');
setState(() {
this._content = null;
});
}
} on FormatException {
showSnackBar('User pressed "back" button before scanning');
setState(() {
this._content = null;
});
} catch (e) {
showSnackBar('Error: $e');
setState(() {
this._content = null;
});
}
}
Please find the repo.
If you want to take a look at Flutter you can find some good examples at our companie’s Github page. Also, you can check our company's page FlutterDevs.

Navigating from a plugin callback

The problem is when I want to navigate from a callback - which is invoked by plugin - new page is pushed in as a widget inside my page.
This is the code:
import 'dart:async';
import 'package:barcode_scan/barcode_scan.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(new MaterialApp(
home: new MyApp(),
) );
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
String barcode = "";
#override
initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Barcode Scanner Example'),
),
body: new Center(
child: new Column(
children: <Widget>[
new Container(
child: new MaterialButton(
onPressed: scan, child: new Text("Scan")),
padding: const EdgeInsets.all(8.0),
),
],
),
));
}
Future scan() async {
try {
String barcode = await BarcodeScanner.scan();
print("${context}");
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return new BarcodePage(barcode);}
));
setState(() => this.barcode = barcode);
} on PlatformException catch (e) {
if (e.code == BarcodeScanner.CameraAccessDenied) {
setState(() {
this.barcode = 'The user did not grant the camera permission!';
});
} else {
setState(() => this.barcode = 'Unknown error: $e');
}
} on FormatException{
setState(() => this.barcode = 'null (User returned using the "back"-button before scanning anything. Result)');
} catch (e) {
setState(() => this.barcode = 'Unknown error: $e');
}
}
}
class BarcodePage extends StatefulWidget {
BarcodePage(String s) {
str = s;
}
String str;
#override
State<StatefulWidget> createState() {
return _BarcodePageState(str);
}
}
class _BarcodePageState extends State<BarcodePage> {
String str;
_BarcodePageState(String s ){
str = s;
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("Bar code"),),
body: new Text(str),
);
}
}
You can find the application in https://github.com/arashbi/flutter_barcode_reader example folder
This is related to my question before, but simpler setup.
AFAI understand the problem is that callback is happening in the middle of render pipeline, and it causes the wrong behaviour. The solution is to use either Future.delayed or SchedulerBinding.instance.addPostFrameCallback
These methods causes the navigation to happen after the render pipeline, and the navigator can do its job properly