How to use FutureBuilder with intro slider? - android

I'm using the flutter package: https://pub.dev/packages/intro_slider and once the intro has already been viewed the first time the user has logged, I don't want the intro to show on subsequent app opens. There is a visual error where the intro briefly appears before switching to the main app on the subsequent opens, so I tried to use FutureBuilder, but I'm quite inexperienced.
Here is where the widget is being built:
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: restore(),
builder: (BuildContext context, snapshot){
return new IntroSlider(
// List slides
slides: this.slides,
// Skip button
renderSkipBtn: this.renderSkipBtn(),
onSkipPress: this.onSkipPress,
highlightColorSkipBtn: Color(0xff000000),
// Next, Done button
onDonePress: this.onDonePress,
renderNextBtn: this.renderNextBtn(),
renderDoneBtn: this.renderDoneBtn(),
highlightColorDoneBtn: Color(0xff000000),
// Dot indicator
colorDot: Colors.white10,
colorActiveDot: Colors.greenAccent,
sizeDot: 8.0,
);
}
);
}
}
Here is my restore function:
Future<String> restore() async {
final SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
setState(() {
yn = (sharedPrefs.getBool('yn') ?? false);
});
if (yn){
Navigator.pushReplacement(context, new MaterialPageRoute(
builder: (context) => SplashScreen()
));
}else{
await sharedPrefs.setBool('yn', true);
}
}
And here's my initState if it matters:
void initState() {
super.initState();
restore();
slides.add(
new Slide(
title: "Hail",
styleTitle:
TextStyle(color: Colors.white, fontSize: 30.0, fontFamily: 'Courier'),
description: "Allow miles wound place the leave had. To sitting subject no improve studied limited",
styleDescription:
TextStyle(color: Colors.white, fontSize: 20.0, fontFamily: 'Mono'),
pathImage: "assets/totoro.png",
backgroundColor: Colors.black,
),
);
}
The yn variable is just to check if it is the user's first time opening the app. Thanks in advance!!

Related

Change random background image by clicking a button flutter (dart)

I have a list of images, and a function that picks an image from that list randomly:
AssetImage imagePicker() {
Random randomNumberGen = Random();
int index = randomNumberGen.nextInt(bgImgList.length);
return AssetImage(bgImgList[index]);
}
And I want a button that when clicking it will call this function and refresh the screen.
floatingActionButton: FloatingActionButton(
onPressed: () { imagePicker(); },
child: const Text(
'change picture' ,
textAlign: TextAlign.center,
),
The issue is the function is called, but the widget i have is not refreshing so the picture doesn't change
this is the widget code:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Israel Geography'),
centerTitle: true,
backgroundColor: Colors.blue[900],
),
body: Center(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imagePicker(),
fit: BoxFit.cover
),
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: () { imagePicker(); },
child: const Text(
'change picture' ,
textAlign: TextAlign.center,
),
),
);
}
Technically, you are calling the imagePicker() method twice, and there is also no state that is holding the final picked image.
Also, this makes the screen not static anymore. The displayed image is changing on each button click, so there is dynamic information in your UI now, so you need to convert your Stateless widget into a Stateful one so you can do setState() whenever the visible information changes.
So after converting to Stateful,
your State class should have a variable like
AssetImage pickedImage = AssetImage(...); // a default image
And in your imagePicker() method, you can assign the pickedImage var with the chosen image instead of returning it.
AssetImage imagePicker() {
Random randomNumberGen = Random();
int index = randomNumberGen.nextInt(bgImgList.length);
// this will rebuild your UI
setState(() {
pickedImage = AssetImage(bgImgList[index]);
});
}
And in your widget, instead of this:
image: imagePicker(),
Do this:
image: pickedImage,
And every time on button click, you pick a new image, rebuild the UI because of setState and now pickedImage will be pointing to another image.
You need the state for a random image. StatefulWidget is one way to accomplish that.
class ImagePicker {
static Image random() {
return Image.network('https://picsum.photos/500/300?andom=${DateTime.now().millisecondsSinceEpoch}');
}
}
class ImagePickerWidget extends StatefulWidget {
const ImagePickerWidget();
#override
State<ImagePickerWidget> createState() => _ImagePickerWidgetState();
}
class _ImagePickerWidgetState extends State<ImagePickerWidget> {
Image _random = ImagePicker.random();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: _random),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _random = ImagePicker.random()),
child: const Icon(Icons.refresh),
),
);
}
}
If you want to keep a widget stateless, provider is one way to that. See Simple app state management for details.

Agora in Flutter- navigating to the video chat screen more than one time keeps local video loading forever

I am using Agora for a one-to-one video chat purpose in Flutter.
User1 has an app to go online and user2 has another app to go online. After both of them go online, they can do video chat with one another. Both apps have almost similar codebase.
I have a screen or activity (say screen1) where an alert dialog is shown on tapping a button (say button1). On tapping the Continue button in the alert dialog, the dialog disappears and the user is taken to the screen (say screen2) where the video chat takes place. But after going to the video chat screen successfully, if the user taps on the back button on the mobile set then s/he is taken to screen1 and after tapping on button1, if the user taps on the Continue button in the popped up alert dialog, the user is again taken to screen2 but this time the local video (i.e. video of the user using the app) keeps loading for ever. Obviously I want the local video to load as it did for the first time.
I am gonna put my code here in such a way that you can easily run that.
Following code is for user1. For user2, no alert box is there in the app. Same code from user1 is used for user2 app, except the value of remoteUid is set to be 2 for user2 while this value is set to be 1 for user1. These are just two values identifying 2 users.
For user1:
main.dart:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'livesession1to1.dart';
void main() {
runApp(MessagingExampleApp());
}
class NavigationService {
static GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
}
/// Entry point for the example application.
class MessagingExampleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Messaging Example App',
navigatorKey: NavigationService.navigatorKey, // set property
theme: ThemeData.dark(),
routes: {
'/': (context) => Application(),
'/liveSession1to1': (context) =>LiveSession1to1(),
},
);
}
}
int _messageCount = 0;
/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
String constructFCMPayload(String? token, String server_key) {
_messageCount++;
return jsonEncode({
'token': token,
'to':token,
'data': {
'via': 'FlutterFire Cloud Messaging!!!',
'count': _messageCount.toString(),
},
'notification': {
'title': 'Hello FlutterFire!',
'body': 'This notification (#$_messageCount) was created via FCM! =============',
},
"delay_while_idle" : false,
"priority" : "high",
"content_available" : true
});
}
/// Renders the example application.
class Application extends StatefulWidget {
#override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
String? _token;
#override
void initState() {
super.initState();
}
showAlertDialog() {
BuildContext context=NavigationService.navigatorKey.currentContext!;
// set up the buttons
Widget cancelButton = TextButton(
child: Text("Cancel"),
onPressed: () {},
);
Widget continueButton = TextButton(
child: Text("Continue"),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context).pushNamed('/liveSession1to1');
},
);
Timer? timer = Timer(Duration(milliseconds: 5000), (){
Navigator.of(context, rootNavigator: true).pop();
});
showDialog(
context: context,
builder: (BuildContext builderContext) {
return AlertDialog(
backgroundColor: Colors.black26,
title: Text('One to one live session'),
content: SingleChildScrollView(
child: Text('Do you want to connect for a live session ?'),
),
actions: [
cancelButton,
continueButton,
],
);
}
).then((value){
// dispose the timer in case something else has triggered the dismiss.
timer?.cancel();
timer = null;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('My App'),
),
floatingActionButton: Builder(
builder: (context) => FloatingActionButton(
onPressed: showAlertDialog,
backgroundColor: Colors.white,
child: const Icon(Icons.send),
),
),
body: SingleChildScrollView(
child: Text(
'Trigger Alert'
),
),
);
}
}
livesession1to1.dart:
import 'dart:async';
import 'package:agora_rtc_engine/rtc_engine.dart';
import 'package:agora_rtc_engine/rtc_local_view.dart' as RtcLocalView;
import 'package:agora_rtc_engine/rtc_remote_view.dart' as RtcRemoteView;
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
// const appId = "<-- Insert App Id -->";
// const token = "<-- Insert Token -->";
const appId = "......";// Put Agora App ID from Agora site here
const token = "....";// Put token ( temporary token avilable from Agora site)
void main() => runApp(MaterialApp(home: LiveSession1to1()));
class LiveSession1to1 extends StatefulWidget {
#override
_LiveSession1to1State createState() => _LiveSession1to1State();
}
class _LiveSession1to1State extends State<LiveSession1to1> {
int _remoteUid=1;
bool _localUserJoined = false;
late RtcEngine _engine;
#override
void initState() {
super.initState();
setState(() {});
initAgora();
}
Future<void> initAgora() async {
// retrieve permissions
await [Permission.microphone, Permission.camera].request();
// Create RTC client instance
RtcEngineContext context = RtcEngineContext(appId);
_engine = await RtcEngine.createWithContext(context);
await _engine.enableVideo();
_engine.setEventHandler(
RtcEngineEventHandler(
joinChannelSuccess: (String channel, int uid, int elapsed) {
print("local user $uid joined");
setState(() {
_localUserJoined = true;
});
},
userJoined: (int uid, int elapsed) {
print("remote user $uid joined");
setState(() {
_remoteUid = uid;
});
},
userOffline: (int uid, UserOfflineReason reason) {
print("remote user $uid left channel");
setState(() {
// _remoteUid = null;
_remoteUid = 0;
});
},
),
);
try {
await _engine.joinChannel(token, "InstaClass", null, 0);
} catch (e) {
print("error with agora = ");
print("$e");
print("error printeddddd");
}
}
// Create UI with local view and remote view
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Agora Video Call'),
),
body: Stack(
children: [
Center(
child: _remoteVideo(),
),
Align(
alignment: Alignment.topLeft,
child: Container(
width: 100,
height: 150,
child: Center(
child: _localUserJoined
? RtcLocalView.SurfaceView()
: CircularProgressIndicator(),
),
),
),
],
),
);
}
// Display remote user's video
Widget _remoteVideo() {
if (_remoteUid != 0) {
return RtcRemoteView.SurfaceView(
uid: _remoteUid,
channelId: "InstaClass",
);
}else {
print("'Please wait for remote user to join',");
return Text(
'Please wait for remote user to join',
textAlign: TextAlign.center,
);
}
}
}
For user2:
main.dart:
import 'dart:async';
import 'package:agora_rtc_engine/rtc_engine.dart';
import 'package:agora_rtc_engine/rtc_local_view.dart' as RtcLocalView;
import 'package:agora_rtc_engine/rtc_remote_view.dart' as RtcRemoteView;
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
const appId = "....."; // Same as user1 app
const token = "....."; // same as user1 app
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// int? _remoteUid=1;
int _remoteUid=2;
bool _localUserJoined = false;
late RtcEngine _engine;
#override
void initState() {
super.initState();
initAgora();
}
Future<void> initAgora() async {
// retrieve permissions
await [Permission.microphone, Permission.camera].request();
//create the engine
_engine = await RtcEngine.create(appId);
await _engine.enableVideo();
_engine.setEventHandler(
RtcEngineEventHandler(
joinChannelSuccess: (String channel, int uid, int elapsed) {
print("local user $uid joined");
setState(() {
_localUserJoined = true;
});
},
userJoined: (int uid, int elapsed) {
print("remote user $uid joined");
setState(() {
_remoteUid = uid;
});
},
userOffline: (int uid, UserOfflineReason reason) {
print("remote user $uid left channel");
setState(() {
// _remoteUid = null;
_remoteUid = 0;
});
},
),
);
// await _engine.joinChannel(token, "test", null, 0);
await _engine.joinChannel(token, "InstaClass", null, 0);
}
// Create UI with local view and remote view
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Agora Video Call'),
),
body: Stack(
children: [
Center(
child: _remoteVideo(),
),
Align(
alignment: Alignment.topLeft,
child: Container(
width: 100,
height: 150,
child: Center(
child: _localUserJoined
? RtcLocalView.SurfaceView()
: CircularProgressIndicator(),
),
),
),
],
),
);
}
// Display remote user's video
Widget _remoteVideo() {
/*if (_remoteUid != null) {
return RtcRemoteView.SurfaceView(uid: _remoteUid!);
}*/
if (_remoteUid != 0) {
return RtcRemoteView.SurfaceView(
uid: _remoteUid,
channelId: "InstaClass",
);
}else {
return Text(
'Please wait for remote user to join',
textAlign: TextAlign.center,
);
}
}
}
In order to get the app ID and token, login to Agora site. After logging in, go to the 'Project Management' section to see the projects already created there. Under the Functions column, click on the key symbol and you will be taken to a page where you can generate a temporary token. On that page, give the channel name input the value 'InstaClass' as I have used this name in my code.
How to make the video chat work smoothly after the first time it works well ?
I think the problem is that when pressing back button you are just being taken to the previous screen and the call session is not being end. You can try by leaving the channel when pressing back button like :
_engine.leaveChannel();
End Call button sample
ElevatedButton(
onPressed: () {
_rtcEngine.leaveChannel();
Navigator.pop(context);
},
style: ButtonStyle(
shape: MaterialStateProperty.all(CircleBorder()),
backgroundColor: MaterialStateProperty.all(Colors.red),
padding: MaterialStateProperty.all(
EdgeInsets.fromLTRB(15, 15, 15, 12)),
),
child: Icon(
Icons.phone,
size: 30,
),
)
Back Button override using WillPopScope
return WillPopScope(
onWillPop: () async {
_rtcEngine.leaveChannel();
return true;
},
child: Scaffold(
body: Container(),
),
);

How to pass the data get from Home widget to it's child widgets in flutter?

I am developing an app for a community with login, signup, meetings and chats in flutter. After login successful, the route goes to Home which has five bottom navigation. I am using Firebase in this app for Authentication and Firestore.
I would like to fetch the data once when Home Component started and pass the data to other five bottom navigation bar components.
Now I am fetching the data whenever I switched between navigation components. This increase the Firestore reads.
I tried passing the data through components using constructor variables. But this doesn't work. It shows error that data can't be passed to bottom navigation components Here is my code.
Home.dart
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
User currentUser;
String userId;
Home({this.currentUser, this.userId});
}
class _HomeState extends State<Home> {
CurrentUser userInfo;
DocumentSnapshot doc;
int _selectedIndex = 0;
List<String> upcoming_seven_days;
FirestoreService _firestoreService = FirestoreService();
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static List<Widget> _widgetOptions = <Widget>[
Dashboard(),
MeetingList(),
EventList(),
Chat(),
Profile(),
];
static const List<Widget> _appBarText = <Widget>[
Text(
'Dashboard',
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 26,
),
),
Text(
'Meetings',
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 26,
),
),
Text(
'Events',
style: TextStyle(fontWeight: FontWeight.w300, fontSize: 26),
),
Text(
'Chat',
style: TextStyle(fontWeight: FontWeight.w300, fontSize: 26),
),
Text(
'Profile',
style: TextStyle(fontWeight: FontWeight.w300, fontSize: 26),
),
];
#override
void initState() {
// TODO: implement initState
super.initState();
//setCurrentUserID(widget.currentUser.uid);
//setCurrentUserData(doc.data());
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: _appBarText.elementAt(_selectedIndex)),
body: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
width: double.maxFinite,
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.dashboard),
title: Text('Dashboard'),
backgroundColor: Colors.black),
BottomNavigationBarItem(
icon: Icon(Icons.people),
title: Text('Meetings'),
backgroundColor: Colors.black),
BottomNavigationBarItem(
icon: Icon(Icons.calendar_view_day),
title: Text('Events'),
backgroundColor: Colors.black),
BottomNavigationBarItem(
icon: Icon(Icons.chat),
title: Text('Chat'),
backgroundColor: Colors.black),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Profile'),
backgroundColor: Colors.black),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.lightBlue[200],
onTap: _onItemTapped,
elevation: 8.0,
backgroundColor: Colors.black,
),
);
}
}
This is where I fetch the data from Firestore whenever the user switch to Meeting list component. I don't want to do like that. Rather, I want to pass the respective data from Home to other components. And it should be snapshot, so it can listen to changes.
MeetingList.dart
class MeetingList extends StatelessWidget {
var userInfo;
FirebaseAuth firebaseAuth = FirebaseAuth.instance;
Future getuserinfo() async {
// final uid = firebaseAuth.currentUser.uid;
// userinfo = await firestoreService.getCurrentUserInfo(uid);
// userinfo = userinfo.data().length;
// //print(userinfo);
// return uid;
final uid = firebaseAuth.currentUser.uid;
DocumentSnapshot user = await FirebaseFirestore.instance
.collection('userProfiles')
.doc(uid)
.get();
userInfo = user.data();
return userInfo;
}
#override
Widget build(BuildContext context) {
CollectionReference meetings =
FirebaseFirestore.instance.collection('meetings');
return FutureBuilder(
future: getuserinfo(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return LoadingIndicator();
} else {
return StreamBuilder<QuerySnapshot>(
stream: meetings.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return LoadingIndicator();
}
return new ListView(
children: snapshot.data.docs.map((DocumentSnapshot document) {
String meetingRole = document.data()['role'];
var userRole = userInfo['role'];
print(userRole);
if (meetingRole == 'all' || meetingRole == userRole) {
return Meeting_Card(
meeting: document.data(),
);
} else {
return Container();
}
}).toList(),
);
},
);
}
},
);
}
}
Your help would be so much helpful for the community.
You can use Provider package for this, which is a wrapper around the InheritedWidget in flutter.
InheritedWidget is used to efficiently propagate information down
the tree, without having to pass them through various constructors
down the widget tree.
You can find more information about InheritedWidget here.
Provider package is wrapper around InheritedWidget to make them
easier to use and more reusable.
More information on Provider in the documentation here
To implement your solution using Provider:
Create a ChangeNotifier class called UserProvider to hold the data you want common between all the children widgets:
class UserProvider extends ChangeNotifier {
User userInfo;
Future getuserinfo() async {
// final uid = firebaseAuth.currentUser.uid;
// userinfo = await firestoreService.getCurrentUserInfo(uid);
// userinfo = userinfo.data().length;
// //print(userinfo);
// return uid;
final uid = firebaseAuth.currentUser.uid;
DocumentSnapshot user = await FirebaseFirestore.instance
.collection('userProfiles')
.doc(uid)
.get();
userInfo = user.data();
return userInfo;
}
}
Now wrap your Home Widget in a ChangeNotifierProvider widget:
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<UserProvider>(
lazy: false,
create: (context) => UserProvider(),
child: Home(),
);
}
}
Now you can access the content of the UserProvider class from wherever down the same widget tree (Any of the tabs) by using:
/// Get an instance of the UserProvider in the ancestors of the current widget tree like this.
UserProvider userProvider = Provider.of<UserProvider>(context);
/// Call any method inside the UserProvider class like this
userProvider.getUserInfo();
/// access any data variables inside the UserProvider class like this.
User userInfo = userProvider.userInfo;
You can also take a look at the Consumer and Selector widgets in the provider package, which provide an efficient ways to redraw the UI based on certain parameters of the ChangeNotifier class, when the notifyListeners() methid is called from the ChangeNotifier class.

Flutter show's a red error screen before loading data from firebase using StreamBuilder, how to fix?

I am getting data from firebase and making cards using that data. to display certain data of user
from database, the data loads fine but for 1 second or more there is a red error screen. I want this to go away I'll show you my code. can somebody please help show me what's wrong. I have no clue what I am doing wrong that this happens and I get the error
The getter 'uid' was called on null.
Receiver: null
Tried calling: uid
The method '[]' was called on null.
Receiver: null
Tried calling:
here is my code, any help will be appreciated thankyou
class CardBuilder extends StatefulWidget {
final String title;
final String text;
final IconData icon;
CardBuilder({Key key, this.title,this.text,this.icon}): super(key: key);
#override
_CardBuilderState createState() => _CardBuilderState();
}
class _CardBuilderState extends State<CardBuilder> {
FirebaseUser user;
String error;
void setUser(FirebaseUser user) {
setState(() {
this.user = user;
this.error = null;
});
}
void setError(e) {
setState(() {
this.user = null;
this.error = e.toString();
});
}
#override
void initState() {
super.initState();
FirebaseAuth.instance.currentUser().then(setUser).catchError(setError);
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Firestore.instance.collection(UserInformation).document(user.uid).snapshots(),
builder: (context, snapshot) {
var userDocument = snapshot.data;
// User userDocument here ..........
return Card(
margin: EdgeInsets.only(left: 20,right: 20,top: 20),
child: ListTile(
leading:
Icon(widget.icon, color: QamaiGreen, size: 20.0),
title: AutoSizeText(
'${widget.title} : ${userDocument[widget.text]}',
style: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.w600,
color: QamaiThemeColor,
fontSize: 15
),
maxLines: 1,
maxFontSize: 15,
minFontSize: 12,
),
));
});
}
}
So after trying out some stuff I finally fixed this error. decided to post an answer since this question doesn't have any solution online. at-least I couldn't find one. hope this helps people having the same issue
what I first did was declare a String which contained the value of user.uid
String userid;
void _getUser() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
userid=user.uid;
}
now in the card builder widget I got rid of the extra stuff and simply called _getUser() method in the init state
class _CardBuilderState extends State<CardBuilder> {
#override
void initState() {
super.initState();
_getUser();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Firestore.instance.collection(UserInformation).document(userid).snapshots(),
builder: (context, snapshot) {
if(snapshot.hasData)
{
final userDocument = snapshot.data;
final title=userDocument[widget.text];
return Card(
margin: EdgeInsets.only(left: 20,right: 20,top: 20),
child: ListTile(
leading:
Icon(widget.icon, color: QamaiGreen, size: 20.0),
title: AutoSizeText(
'${widget.title} : $title',
style: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.w600,
color: QamaiThemeColor,
fontSize: 15
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
maxFontSize: 15,
minFontSize: 9,
),
));
}
else{
return Card(
margin: EdgeInsets.only(left: 20,right: 20,top: 20),
child: ListTile(
leading:
Icon(widget.icon, color: QamaiGreen, size: 20.0),
title: AutoSizeText(
'LOADING...',
style: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.w600,
color: QamaiThemeColor,
fontSize: 15
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
maxFontSize: 15,
minFontSize: 9,
),
));
}
});
}
}
Last changes were to add a condition checking if the snapshot has data or not. also saving the specific document field in a final variable helped a lot in my case that is
final title=userDocument[widget.text];
and finally an else condition was added to mimic the same card interface that shows a text which says loading. you can use CirculorIndicator if you want that will also work for me this was looking more natural for the interface.
you can show a progress indicator as your load your data
as shown below
return StreamBuilder(
stream: Firestore.instance.collection(UserInformation).document(user.uid).snapshots(),
builder: (context, snapshot) {
//==========show progress============
if (!snapshot.hasData) {
return CircularProgressIndicator()
}
var userDocument = snapshot.data;
// User userDocument here ..........
try this in main.dart:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
When the device doesn't have proper internet connection or when the device is offline, flutter red error screen is displayed.
You can try using a CircularProgressIndicator as data loads from the API.
String userid;
void _getUser() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
userid=user.uid;
}
Ensure you initialize the _getUser method.
#override
void initState() {
getUser();
super.initState();
}
You can now build your widget.
userid == null ? CircularProgressIndicator() : yourWidget(),

How to implement a swipe to delete listview to remove data from firestore

Im very new to flutter and dart so this might be a basic question. However, what I would like to know is how to implement a swipe to delete method in a listview to delete data from firestore too.
I tried using the Dissmissible function but i dont understand how to display the list and I cant seem to understand how to remove the selected data as well.
This here is my dart code
Widget build(BuildContext context) {
return new Scaffold(
resizeToAvoidBottomPadding: false,
appBar: new AppBar(
centerTitle: true,
automaticallyImplyLeading: false,
title: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children:
<Widget>[
Text("INVENTORY",textAlign: TextAlign.center,) ,new IconButton(
icon: Icon(
Icons.home,
color: Colors.black,
),
onPressed: () {
Navigator.push(
context,
SlideLeftRoute(widget: MyHomePage()),
);
})]),
),body: ListPage(),
);
}
}
class ListPage extends StatefulWidget {
#override
_ListPageState createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
Future getPosts() async{
var firestore = Firestore.instance;
QuerySnapshot gn = await
firestore.collection("Inventory").orderBy("Name",descending:
false).getDocuments();
return gn.documents;
}
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: getPosts(),
builder: (_, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: Text("Loading"),
);
}else{
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:(_, index){
return EachList(snapshot.data[index].data["Name"].toString(),
snapshot.data[index].data["Quantity"]);
});
}
}),
);
}
}
class EachList extends StatelessWidget{
final String details;
final String name;
EachList(this.name, this.details);
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Card(
child:new Container(
padding: EdgeInsets.all(8.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Row(
children: <Widget>[
new CircleAvatar(child: new Text(name[0].toUpperCase()),),
new Padding(padding: EdgeInsets.all(10.0)),
new Text(name, style: TextStyle(fontSize: 20.0),),
],
),
new Text(details, style: TextStyle(fontSize: 20.0))
],
),
),
);
}
}
You should use Dismissible widget. I used it for an inbox list retrieved from Firestore. Inside your EachList return something like this
return Dismissible(
direction: DismissDirection.startToEnd,
resizeDuration: Duration(milliseconds: 200),
key: ObjectKey(snapshot.documents.elementAt(index)),
onDismissed: (direction) {
// TODO: implement your delete function and check direction if needed
_deleteMessage(index);
},
background: Container(
padding: EdgeInsets.only(left: 28.0),
alignment: AlignmentDirectional.centerStart,
color: Colors.red,
child: Icon(Icons.delete_forever, color: Colors.white,),
),
// secondaryBackground: ...,
child: ...,
);
});
IMPORTANT: in order to remove the list item you'll need to remove the item from the snapshot list as well, not only from firestore:
_deleteMessage(index){
// TODO: here remove from Firestore, then update your local snapshot list
setState(() {
snapshot.documents.removeAt(index);
});
}
Here the doc: Implement Swipe to Dismiss
And here a video by Flutter team: Widget of the week - Dismissilbe
You can use the flutter_slidable package to achieve the same.
You can also check out my Cricket Team on Github in which I have did the same you want to achieve, using same package.
Example for how to use package are written here.
I'd like to add that when deleting a document from Firestore, no await is needed as the plugin automatically caches the changes and then syncs them up when there is a connection again.
For instance, I used to use this method
Future deleteWatchlistDocument(NotifierModel notifier) async {
final String uid = await _grabUID();
final String notifierID = notifier.documentID;
return await _returnState(users.document(uid).collection(watchlist).document(notifierID).delete());
}
in which I was waiting for the call to go through, however this prevented any other call to go through and only allowed one. Removing this await tag however solved my issue.
Now I can delete documents offline, and the changes will sync up with Firestore when a connection is regained. It's pretty cool to watch in the console.
I'd recommend watching this video about offline use with Firestore

Categories

Resources