I have a flutter project chat with firebase realtime database and when I open a chat with a user, firebase take 1-3 seconds to load the conversation.
So, I have to insert a progress indicator when firebase is retrieving data on chat opended.
This is the class:
class ChatView extends StatefulWidget {
const ChatView({
Key key,
#required this.itemId,
#required this.chatFlag,
#required this.buyerUserId,
#required this.sellerUserId,
}) : super(key: key);
final String itemId;
final String chatFlag;
final String buyerUserId;
final String sellerUserId;
// final String isOffer;
#override
_ChatViewState createState() => _ChatViewState();
}
enum ChatUserStatus { active, in_active, offline }
class _ChatViewState extends State<ChatView>
with SingleTickerProviderStateMixin, WidgetsBindingObserver {
AnimationController animationController;
DatabaseReference _messagesRef;
DatabaseReference _chatRef;
DatabaseReference _userPresence;
final bool _anchorToBottom = true;
FirebaseApp firebaseApp;
PsValueHolder psValueHolder;
String sessionId;
ChatHistoryRepository chatHistoryRepository;
NotificationRepository notiRepo;
UserUnreadMessageRepository userUnreadMessageRepository;
GalleryRepository galleryRepo;
ProductRepository productRepo;
GetChatHistoryProvider getChatHistoryProvider;
UserUnreadMessageProvider userUnreadMessageProvider;
ChatHistoryListProvider chatHistoryListProvider;
ItemDetailProvider itemDetailProvider;
GalleryProvider galleryProvider;
NotificationProvider notiProvider;
SyncChatHistoryParameterHolder holder;
GetChatHistoryParameterHolder getChatHistoryParameterHolder;
PsResource<ChatHistory> chatHistory;
String lastTimeStamp;
int lastAddedDateTimeStamp;
String status = '';
String itemId;
String receiverId;
String senderId;
String otherUserId;
ChatUserStatus isActive;
TextEditingController messageController = TextEditingController();
Future<FirebaseApp> configureDatabase() async {
WidgetsFlutterBinding.ensureInitialized();
final FirebaseApp app = await Firebase.initializeApp(
options: Platform.isIOS
? const FirebaseOptions(
appId: Config.iosGoogleAppId,
messagingSenderId: Config.iosGcmSenderId,
databaseURL: Config.iosDatabaseUrl,
projectId: Config.iosProjectId,
apiKey: Config.iosApiKey)
: const FirebaseOptions(
appId: Config.androidGoogleAppId,
apiKey: Config.androidApiKey,
projectId: Config.androidProjectId,
messagingSenderId: Config.androidGcmSenderId,
databaseURL: Config.androidDatabaseUrl,
),
);
return app;
}
#override
void initState() {
super.initState();
configureDatabase().then((FirebaseApp app) {
firebaseApp = app;
});
final FirebaseDatabase database = FirebaseDatabase(app: firebaseApp);
_messagesRef = database.reference().child('Message');
_chatRef = database.reference().child('Current_Chat_With');
_userPresence = database.reference().child('User_Presence');
if (database != null && database.databaseURL != null) {
database.setPersistenceEnabled(true);
database.setPersistenceCacheSizeBytes(10000000);
}
animationController =
AnimationController(duration: PsConfig.animation_duration, vsync: this);
WidgetsBinding.instance.addObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
// user returned to our app
} else if (state == AppLifecycleState.inactive) {
_chatRef.child(psValueHolder.loginUserId).remove();
// app is inactive
} else if (state == AppLifecycleState.paused) {
_chatRef.child(psValueHolder.loginUserId).remove();
// user is about quit our app temporally
} else if (state == AppLifecycleState.detached) {
// app suspended (not used in iOS)
}
}
#override
void dispose() {
super.dispose();
if (mounted) {
_chatRef.child(psValueHolder.loginUserId).remove();
_userPresence.child(psValueHolder.loginUserId).remove();
}
WidgetsBinding.instance.removeObserver(this);
Utils.isReachChatView = false;
}
Future<bool> resetUnreadMessageCount(
ChatHistoryListProvider chatHistoryListProvider,
PsValueHolder valueHolder,
UserUnreadMessageProvider userUnreadMessageProvider) async {
final ResetUnreadMessageParameterHolder resetUnreadMessageParameterHolder =
ResetUnreadMessageParameterHolder(
itemId: widget.itemId,
buyerUserId: widget.buyerUserId,
sellerUserId: widget.sellerUserId,
type: widget.chatFlag == PsConst.CHAT_FROM_BUYER
? PsConst.CHAT_TO_SELLER
: PsConst.CHAT_TO_BUYER);
final dynamic _returnData = await chatHistoryListProvider
.resetUnreadMessageCount(resetUnreadMessageParameterHolder.toMap());
if (_returnData == null) {
if (valueHolder.loginUserId != null && valueHolder.loginUserId != '') {
final UserUnreadMessageParameterHolder userUnreadMessageHolder =
UserUnreadMessageParameterHolder(
userId: valueHolder.loginUserId,
deviceToken: valueHolder.deviceToken);
userUnreadMessageProvider
.userUnreadMessageCount(userUnreadMessageHolder);
}
return true;
} else {
return false;
}
}
Future<void> _insertDataToFireBase(
String id,
bool isSold,
bool isUserBought,
String itemId,
String message,
int offerStatus,
String sendByUserId,
String sessionId,
int type,
) async {
final Message messages = Message();
messages.addedDate = Utils.getTimeStamp();
messages.id = id;
messages.isSold = isSold;
messages.isUserBought = isUserBought;
messages.itemId = itemId;
messages.message = message;
messages.offerStatus = offerStatus;
messages.sendByUserId = sendByUserId;
messages.sessionId = sessionId;
messages.type = type;
final String newkey = _messagesRef.child(sessionId).push().key;
messages.id = newkey;
_messagesRef
.child(sessionId)
.child(newkey)
.set(messages.toInsertMap(messages));
}
Future<void> _deleteDataToFireBase(
String id,
bool isSold,
String itemId,
String message,
String sendByUserId,
String sessionId,
) async {
final Message messages = Message();
messages.addedDate = Utils.getTimeStamp();
messages.id = id;
messages.isSold = isSold;
messages.itemId = itemId;
messages.message = message;
messages.sendByUserId = sendByUserId;
messages.sessionId = sessionId;
final String key =
_messagesRef.child(sessionId).child(id).remove().toString();
messages.id = key;
_messagesRef
.child(sessionId)
.child(key)
.set(messages.toDeleteMap(messages));
}
Future<void> _updateDataToFireBase(
int addedDate,
String id,
bool isSold,
bool isUserBought,
String itemId,
String message,
int offerStatus,
String sendByUserId,
String sessionId,
int type,
) async {
final Message messages = Message();
messages.id = id;
messages.isSold = isSold;
messages.isUserBought = isUserBought;
messages.itemId = itemId;
messages.message = message;
messages.offerStatus = offerStatus;
messages.sendByUserId = sendByUserId;
messages.sessionId = sessionId;
messages.type = type;
messages.addedDateTimeStamp = addedDate;
_messagesRef
.child(sessionId)
.child(messages.id)
.set(messages.toUpdateMap(messages));
}
Future<void> _insertSenderAndReceiverToFireBase(
String sessionId,
String itemId,
String receiverId,
String senderId,
String userName) async {
final Chat chat =
Chat(itemId: itemId, receiverId: receiverId, senderId: senderId);
_chatRef.child(senderId).set(chat.toMap(chat));
final ChatUserPresence chatUserPresence =
ChatUserPresence(userId: senderId, userName: userName);
_userPresence.child(senderId).set(chatUserPresence.toMap(chatUserPresence));
}
#override
Widget build(BuildContext context) {
const Widget _spacingWidget = SizedBox(
width: PsDimens.space10,
);
lastTimeStamp = null;
psValueHolder = Provider.of<PsValueHolder>(context);
chatHistoryRepository = Provider.of<ChatHistoryRepository>(context);
notiRepo = Provider.of<NotificationRepository>(context);
galleryRepo = Provider.of<GalleryRepository>(context);
productRepo = Provider.of<ProductRepository>(context);
userUnreadMessageRepository =
Provider.of<UserUnreadMessageRepository>(context);
if (psValueHolder.loginUserId != null) {
if (psValueHolder.loginUserId == widget.buyerUserId) {
sessionId =
Utils.sortingUserId(widget.sellerUserId, widget.buyerUserId);
otherUserId = widget.sellerUserId;
} else if (psValueHolder.loginUserId == widget.sellerUserId) {
sessionId =
Utils.sortingUserId(widget.buyerUserId, widget.sellerUserId);
otherUserId = widget.buyerUserId;
}
_insertSenderAndReceiverToFireBase(sessionId, widget.itemId, otherUserId,
psValueHolder.loginUserId, psValueHolder.loginUserName);
}
_chatRef.child(otherUserId).onValue.listen((Event event) {
if (event.snapshot.value == null) {
if (isActive == null || isActive != ChatUserStatus.offline && mounted) {
setState(() {
status = Utils.getString(context, 'chat_view__status_offline');
isActive = ChatUserStatus.offline;
});
}
} else {
itemId = event.snapshot.value['itemId'];
final String _receiverId = event.snapshot.value['receiver_id'];
if (_receiverId == psValueHolder.loginUserId &&
itemId == widget.itemId) {
if (isActive != ChatUserStatus.active && mounted) {
setState(() {
status = Utils.getString(context, 'chat_view__status_active');
isActive = ChatUserStatus.active;
});
}
} else {
if (isActive != ChatUserStatus.in_active && mounted) {
setState(() {
status = Utils.getString(context, 'chat_view__status_inactive');
isActive = ChatUserStatus.in_active;
});
}
}
}
});
Future<void> checkOfferStatus(ChatHistory chatHistory) async {
if (chatHistory != null &&
chatHistory.isOffer == PsConst.ONE &&
chatHistory.isAccept != PsConst.ONE) {
await getChatHistoryProvider
.getChatHistory(getChatHistoryParameterHolder);
}
}
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(0), // here the desired height
child: AppBar(
automaticallyImplyLeading: true,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarIconBrightness: Utils.getBrightnessForAppBar(context),
),
iconTheme: Theme.of(context)
.iconTheme
.copyWith(color: PsColors.textPrimaryColor),
title: Text(
status,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headline6.copyWith(
fontWeight: FontWeight.bold,
color: PsColors.white),
),
backgroundColor: PsColors.mainColor,
bottomOpacity: 0.0,
elevation: 0.0)),
body: PsWidgetWithMultiProvider(
child: MultiProvider(
providers: <SingleChildWidget>[
ChangeNotifierProvider<ItemDetailProvider>(
lazy: false,
create: (BuildContext context) {
itemDetailProvider = ItemDetailProvider(
repo: productRepo, psValueHolder: psValueHolder);
final String loginUserId =
Utils.checkUserLoginId(psValueHolder);
itemDetailProvider.loadProduct(widget.itemId, loginUserId);
return itemDetailProvider;
}),
ChangeNotifierProvider<UserUnreadMessageProvider>(
lazy: false,
create: (BuildContext context) {
userUnreadMessageProvider = UserUnreadMessageProvider(
repo: userUnreadMessageRepository);
return userUnreadMessageProvider;
}),
ChangeNotifierProvider<ChatHistoryListProvider>(
lazy: false,
create: (BuildContext context) {
chatHistoryListProvider =
ChatHistoryListProvider(repo: chatHistoryRepository);
resetUnreadMessageCount(chatHistoryListProvider,
psValueHolder, userUnreadMessageProvider);
return chatHistoryListProvider;
}),
ChangeNotifierProvider<NotificationProvider>(
lazy: false,
create: (BuildContext context) {
notiProvider = NotificationProvider(
repo: notiRepo, psValueHolder: psValueHolder);
return notiProvider;
}),
ChangeNotifierProvider<GalleryProvider>(
lazy: false,
create: (BuildContext context) {
galleryProvider = GalleryProvider(
repo: galleryRepo,
);
return galleryProvider;
}),
ChangeNotifierProvider<GetChatHistoryProvider>(
lazy: false,
create: (BuildContext context) {
getChatHistoryProvider =
GetChatHistoryProvider(repo: chatHistoryRepository);
getChatHistoryParameterHolder = GetChatHistoryParameterHolder(
itemId: widget.itemId,
buyerUserId: widget.buyerUserId,
sellerUserId: widget.sellerUserId);
getChatHistoryProvider
.getChatHistory(getChatHistoryParameterHolder);
return getChatHistoryProvider;
}),
],
child: Consumer<ItemDetailProvider>(builder:
(BuildContext context, ItemDetailProvider itemDetailProvider,
Widget child) {
if (itemDetailProvider.itemDetail != null &&
itemDetailProvider.itemDetail.data != null) {
return Container(
color: Utils.isLightMode(context)
? Colors.grey[100]
: Colors.grey[900],
child: Column(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Container(
alignment: Alignment.topCenter,
width: double.infinity,
child: ItemInfoWidget(
insertDataToFireBase: _insertDataToFireBase,
sessionId: sessionId,
itemData: itemDetailProvider.itemDetail.data,
sendByUserId: psValueHolder.loginUserId ?? '',
chatFlag: widget.chatFlag,
buyerUserId: widget.buyerUserId,
sellerUserId: widget.sellerUserId,
chatHistoryProvider: getChatHistoryProvider,
isOffer:
(getChatHistoryProvider.chatHistory.data !=
null &&
getChatHistoryProvider
.chatHistory.data.id !=
'')
? getChatHistoryProvider
.chatHistory.data.isOffer
: '0',
isUserOnline: isActive == ChatUserStatus.active
? PsConst.ONE
: PsConst.ZERO,
)
)),
Flexible(
child: Container(
margin:
const EdgeInsets.only(bottom: PsDimens.space12),
child: FirebaseAnimatedList(
key: ValueKey<bool>(_anchorToBottom),
query: _messagesRef
.child(sessionId)
.orderByChild('itemId')
.equalTo(widget.itemId),
reverse: _anchorToBottom,
sort: _anchorToBottom
? (DataSnapshot a, DataSnapshot b) {
return b.value['addedDate']
.toString()
.compareTo(
a.value['addedDate'].toString());
}
: null,
itemBuilder: (BuildContext context,
DataSnapshot snapshot,
Animation<double> animation,
int index) {
print('- - - - - - - /nIndex : $index');
bool isSameDate = false;
final Message messages =
Message().fromMap(snapshot.value);
final String chatDateString =
Utils.convertTimeStampToDate(
messages.addedDateTimeStamp);
if (index == 0 || lastTimeStamp == null) {
lastTimeStamp = chatDateString;
lastAddedDateTimeStamp =
messages.addedDateTimeStamp;
}
final DateTime msgDate =
Utils.getDateOnlyFromTimeStamp(
messages.addedDateTimeStamp);
final DateTime lastDate =
Utils.getDateOnlyFromTimeStamp(
lastAddedDateTimeStamp);
if (lastTimeStamp == chatDateString ||
msgDate.compareTo(lastDate) >= 0) {
isSameDate = true;
} else {
isSameDate = false;
}
final Widget _chatCell =
_ChatPageWidget(
buyerUserId: widget.buyerUserId,
sellerUserId: widget.sellerUserId,
chatFlag: widget.chatFlag,
chatHistoryProvider: getChatHistoryProvider,
chatHistoryParameterHolder:
getChatHistoryParameterHolder,
messageObj: messages,
itemDetail:
itemDetailProvider.itemDetail.data,
psValueHolder: psValueHolder,
updateDataToFireBase: _updateDataToFireBase,
insertDataToFireBase: _insertDataToFireBase,
deleteDataToFireBase: _deleteDataToFireBase,
checkOfferStatus: checkOfferStatus,
index: index,
isUserOnline:
isActive == ChatUserStatus.active
? PsConst.ONE
: PsConst.ZERO,
);
Widget _dateWidget;
if (!isSameDate) {
_dateWidget = Container(
margin: const EdgeInsets.only(
top: PsDimens.space8,
bottom: PsDimens.space8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
_spacingWidget,
const Expanded(
child: Divider(
height: PsDimens.space1,
color: Colors.black54),
),
_spacingWidget,
Container(
padding: const EdgeInsets.all(
PsDimens.space4),
decoration: BoxDecoration(
color: Colors.black54,
borderRadius:
BorderRadius.circular(
PsDimens.space8)),
child: Text(
lastTimeStamp,
style: Theme.of(context)
.textTheme
.caption
.copyWith(color: Colors.white),
),
),
_spacingWidget,
const Expanded(
child: Divider(
height: PsDimens.space1,
color: Colors.black54),
),
_spacingWidget,
],
),
);
lastTimeStamp = chatDateString;
lastAddedDateTimeStamp =
messages.addedDateTimeStamp;
}
if (msgDate.compareTo(lastDate) >= 0) {
lastTimeStamp = chatDateString;
lastAddedDateTimeStamp =
messages.addedDateTimeStamp;
}
return isSameDate
? _chatCell
: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_chatCell,
_dateWidget,
],
);
},
),
),
),
],
),
);
} else {
return Container();
}
}))),
);
}
}
Can you suggest me a solution?
Flutter version 2.5.3
Implement a FutureBuilder like this (preferably in the main.dart where you initialize Firebase):
return FutureBuilder(
// Initialize FlutterFire:
future: _initialization,
builder: (context, snapshot) {
// Check for errors
if (snapshot.hasError) {
return SomethingWentWrong();
}
// Once complete, show your application
if (snapshot.connectionState == ConnectionState.done) {
return MyAwesomeApp();
}
// Otherwise, show something whilst waiting for initialization to complete
return Loading();
},
);
For the Loading(); you can either make your own custom widget or just render a CircularProgressIndicator(). Here you can find out how to configure it.
Related
I am registering a user in firebase and each time I try to register it shows me the mentioned error and donot send credentials to the firebase. Although it is getting the credentials from the firebase for login but shows error while storing values in firebase. Below is the code for only register that is getting the email address and password. I have another question that like password and email how could I store other details in firebase e.g Age, Gender etc. Kindly help me go through this.
class _ClientRegistrationScreenState extends State<ClientRegistrationScreen> {
bool showSpinner = false;
final _auth = FirebaseAuth.instance;
File image;
//final ImagePicker _picker = ImagePicker();
String password;
String confirmPassword;
String email;
String name;
bool _passwordVisible = false;
bool _confirmPasswordVisible = false;
#override
void initState() {
_passwordVisible = false;
_confirmPasswordVisible = false;
}
final _formKey = GlobalKey<FormState>();
Expanded(
child: Center(
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return '*Email Address Required';
}
return null;
},
),
),
),
),
Expanded(
child: Center(
child: TextFormField(
onChanged: (value){
password = value;
},
validator: (value) {
if (value == null || value.isEmpty) {
return '*Password Required';
}
if (password != confirmPassword) {
return 'Password Donot Match';
}
return null;
},
onPressed: () {
setState(() {
_passwordVisible = !_passwordVisible;
});
},
),
),
),
),
),
Expanded(
child: Center(
child: TextFormField(
onChanged: (value){
confirmPassword = value;
},
validator: (value) {
if (value == null || value.isEmpty) {
return '*Confirm Password';
}
if (password != confirmPassword) {
return 'Password Donot Match';
}
return null;
},
onPressed: () {
setState(() {
_confirmPasswordVisible = !_confirmPasswordVisible;
});
},
),
),
),
),
),
RoundedButton(
colour: Colors.yellow,
opColour: Colors.yellow.withOpacity(0.2),
title: 'Register',
buttonTextColor: Colors.black,
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() {
showSpinner = true;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
try {
final newUser = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
if(newUser!=null){
Navigator.pushNamed(context, MainEmployeeScreen.id);
print(name);
}
setState(() {
showSpinner = false;
});
}
catch(e){
print(e);
}
}
),
Did you initialize firebase in main function?
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
As per the screenshot once I open the screen it gives me the error "The getter 'email' was called on null." but if I just click back button, then open the screen again, it works well without any error,
the purpose of the screen is the account owner see the notes that was sent to him from the admin account
This is the error appears only one time after opening the app
Here the same screen without any error
Here is the codes in dart where the tap to screen exists
import 'package:flutter/cupertino.dart' show CupertinoIcons;
import 'package:flutter/material.dart';
import 'package:notification_permissions/notification_permissions.dart';
import 'package:provider/provider.dart';
import 'package:rate_my_app/rate_my_app.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../../app.dart';
import '../../common/config.dart';
import '../../common/constants.dart';
import '../../common/tools.dart';
import '../../generated/l10n.dart';
import '../../models/index.dart' show AppModel, User, UserModel, WishListModel;
import '../../routes/flux_navigate.dart';
import '../../screens/blogs/post_screen.dart';
import '../../services/index.dart';
import '../../widgets/common/webview.dart';
import '../custom/smartchat.dart';
import '../index.dart';
import '../users/user_point.dart';
import 'currencies.dart';
import 'language.dart';
import 'notification.dart';
import '../../common/config.dart' as config;
import 'package:cespohm/screens/bywaleed/booking_admin_screen.dart';
import 'package:cespohm/screens/bywaleed/docor_note_tap.dart';
import 'package:cespohm/screens/bywaleed/user_search_new_screen.dart';
import 'package:cespohm/screens/bywaleed/doctor_notes_user_screen.dart';
class SettingScreen extends StatefulWidget {
final List<dynamic> settings;
final String background;
final User user;
final VoidCallback onLogout;
final bool showChat;
SettingScreen({
this.user,
this.onLogout,
this.settings,
this.background,
this.showChat,
});
#override
_SettingScreenState createState() {
return _SettingScreenState();
}
}
class _SettingScreenState extends State<SettingScreen>
with
TickerProviderStateMixin,
WidgetsBindingObserver,
AutomaticKeepAliveClientMixin<SettingScreen> {
#override
bool get wantKeepAlive => true;
final bannerHigh = 150.0;
bool enabledNotification = true;
final RateMyApp _rateMyApp = RateMyApp(
// rate app on store
minDays: 7,
minLaunches: 10,
remindDays: 7,
remindLaunches: 10,
googlePlayIdentifier: kStoreIdentifier['android'],
appStoreIdentifier: kStoreIdentifier['ios']);
void showRateMyApp() {
_rateMyApp.showRateDialog(
context,
title: S.of(context).rateTheApp,
// The dialog title.
message: S.of(context).rateThisAppDescription,
// The dialog message.
rateButton: S.of(context).rate.toUpperCase(),
// The dialog "rate" button text.
noButton: S.of(context).noThanks.toUpperCase(),
// The dialog "no" button text.
laterButton: S.of(context).maybeLater.toUpperCase(),
// The dialog "later" button text.
listener: (button) {
// The button click listener (useful if you want to cancel the click event).
switch (button) {
case RateMyAppDialogButton.rate:
break;
case RateMyAppDialogButton.later:
break;
case RateMyAppDialogButton.no:
break;
}
return true; // Return false if you want to cancel the click event.
},
// Set to false if you want to show the native Apple app rating dialog on iOS.
dialogStyle: const DialogStyle(),
// Custom dialog styles.
// Called when the user dismissed the dialog (either by taping outside or by pressing the "back" button).
// actionsBuilder: (_) => [], // This one allows you to use your own buttons.
);
}
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
await checkNotificationPermission();
});
_rateMyApp.init().then((_) {
// state of rating the app
if (_rateMyApp.shouldOpenDialog) {
showRateMyApp();
}
});
}
// #override
// void dispose() {
// Utils.setStatusBarWhiteForeground(false);
// super.dispose();
// }
Future<void> checkNotificationPermission() async {
if (!isAndroid || isIos) {
return;
}
try {
await NotificationPermissions.getNotificationPermissionStatus()
.then((status) {
if (mounted) {
setState(() {
enabledNotification = status == PermissionStatus.granted;
});
}
});
} catch (err) {
printLog('[Settings Screen] : ${err.toString()}');
}
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
checkNotificationPermission();
}
}
/// Render the Admin Vendor Menu
Widget renderVendorAdmin() {
if (!(widget.user != null ? widget.user.isVendor ?? false : false)) {
return Container();
}
return Card(
color: Theme.of(context).backgroundColor,
margin: const EdgeInsets.only(bottom: 2.0),
elevation: 0,
child: ListTile(
onTap: () {
final String langCode =
Provider.of<AppModel>(context, listen: false).langCode;
if (unsupportedLanguages.contains(langCode)) {
final snackBar = SnackBar(
content: Text(
S.of(context).thisFeatureDoesNotSupportTheCurrentLanguage),
duration: const Duration(seconds: 1),
);
Scaffold.of(context).showSnackBar(snackBar);
return;
}
FluxNavigate.push(
MaterialPageRoute(
builder: (context) =>
Services().widget.getAdminVendorScreen(context, widget.user),
),
forceRootNavigator: true,
);
},
leading: Icon(
Icons.dashboard,
size: 24,
color: Theme.of(context).accentColor,
),
title: Text(
S.of(context).vendorAdmin,
style: const TextStyle(fontSize: 16),
),
trailing: Icon(
Icons.arrow_forward_ios,
size: 18,
color: Theme.of(context).accentColor,
),
),
);
}
/// Render the custom profile link via Webview
/// Example show some special profile on the woocommerce site: wallet, wishlist...
Widget renderWebViewProfile() {
if (widget.user == null) {
return Container();
}
var base64Str = Utils.encodeCookie(widget.user.cookie);
var profileURL = '${serverConfig["url"]}/my-account?cookie=$base64Str';
return Card(
color: Theme.of(context).backgroundColor,
margin: const EdgeInsets.only(bottom: 2.0),
elevation: 0,
child: ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => WebView(
url: profileURL, title: S.of(context).updateUserInfor),
),
);
},
leading: Icon(
CupertinoIcons.profile_circled,
size: 24,
color: Theme.of(context).accentColor,
),
title: Text(
S.of(context).updateUserInfor,
style: const TextStyle(fontSize: 16),
),
trailing: Icon(
Icons.arrow_forward_ios,
color: Theme.of(context).accentColor,
size: 18,
),
),
);
}
Widget renderItem(value) {
IconData icon;
String title;
Widget trailing;
Function() onTap;
bool isMultiVendor = kFluxStoreMV.contains(serverConfig['type']);
switch (value) {
case 'bookingAdmin':
if (widget.user == null ) {
return Container();
}
else if(widget.user.email == config.adminEmail || widget.user.email == config.adminEmailTwo && widget.user != null )
{
icon = FontAwesomeIcons.keyboard;
title = S.of(context).checkout;
trailing =
const Icon(Icons.arrow_forward_ios, size: 18, color: kGrey600);
onTap = () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => const BookingAdminScreen()),
);
}
else {
return Container();
}
break;
case 'addDoctorNote':
if (widget.user == null ) {
return Container();
}
else if(widget.user.isVendor && widget.user != null )
{
icon = FontAwesomeIcons.edit;
title = S.of(context).continueToShipping;
trailing =
const Icon(Icons.arrow_forward_ios, size: 18, color: kGrey600);
onTap = () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => const UserSearchNewScreen()),
);
}
else {
return Container();
}
break;
/// here is the tap to the screen where there is the error
case 'yourDoctorNotes':
if (widget.user == null || widget.user.email == config.adminEmail || widget.user.email == config.adminEmailTwo ) {
return Container();
}
else if(!widget.user.isVendor)
{
icon = FontAwesomeIcons.notesMedical;
title = S.of(context).french;
trailing =
const Icon(Icons.arrow_forward_ios, size: 18, color: kGrey600);
onTap = () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => DoctorNoteUserScreen(senderUser: widget.user,)),
);
}
else {
return Container();
}
break;
case 'chat':
here is the dart code of the screen where is the error
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:date_format/date_format.dart';
import 'package:flutter/material.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_model.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_provider.dart';
import 'package:cespohm/models/entities/user.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:provider/provider.dart';
import '../../generated/l10n.dart' as word;
import '../../models/index.dart' show AppModel, Store, User, UserModel ;
final _fireStore = FirebaseFirestore.instance;
firebase_auth.User loggedInUser;
class DoctorNoteUserScreen extends StatelessWidget {
final User senderUser;
DoctorNoteUserScreen({
Key key,
this.senderUser,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final userSearchProvider = Provider.of<UserSearchProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text(
word.S.of(context).french,
style: TextStyle(
color: Colors.white, fontSize: 18.0, fontWeight: FontWeight.w400),
),
leading: Center(
child: GestureDetector(
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onTap: () => Navigator.pop(context),
),
),
),
body: StreamBuilder<List<UserSearchModel>>(
stream: userSearchProvider.userstwo,
builder: (context, snapshot) {
if (!snapshot.hasData ) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).primaryColor),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(7.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
SizedBox(
height: 40.0,
),
Text(word.S.of(context).french,
style: TextStyle(fontSize: 18.0)),
SizedBox(
height: 15.0,
),
Divider(
thickness: 1.0,
)
],
),
),
Container(
child: ListTile(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 15.0,
),
Container(
child: Text(snapshot.data[index].doctorNote!=null?snapshot.data[index].doctorNote: 'No Notes Yet',
style: TextStyle(fontSize: 20.0)),
),
],
),
),
),
],
),
);
});
}
}),
);
}
}
here is the dart code of the service
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_model.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_provider.dart';
import 'package:cespohm/models/user_model.dart';
class UserSearchService {
final FirebaseFirestore _db = FirebaseFirestore.instance;
final userModel = UserModel();
//Get Entries
Stream<List<UserSearchModel>> getEntries(){
return _db
.collection('users')
.orderBy('firstName', descending: false)
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => UserSearchModel.fromJson(doc.data()))
.toList());
}
/// here is the firebase collection where the error exists
Stream<List<UserSearchModel>> getEntriesTwo(){
return _db
.collection('users').where('email', isEqualTo: userModel.user.email)
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => UserSearchModel.fromJson(doc.data()))
.toList());
}
}
here is the model
import 'package:cloud_firestore/cloud_firestore.dart';
class UserSearchModel {
final String date;
final String firstName;
final String lastName;
final String phone;
final String email;
final String searchKey;
final String doctorNote;
final String bYDoctor;
final String uID;
UserSearchModel(
{this.date,
this.firstName,
this.lastName,
this.phone,
this.email,
this.searchKey,
this.doctorNote,
this.bYDoctor,
this.uID});
// creating a Trip object from a firebase snapshot
UserSearchModel.fromSnapshot(DocumentSnapshot snapshot) :
date = snapshot['date'],
firstName = snapshot['firstName'].toDate(),
lastName = snapshot['lastName'].toDate(),
phone = snapshot['phone'],
email = snapshot['email'],
searchKey = snapshot['searchKey'],
doctorNote = snapshot['doctorNote'],
bYDoctor = snapshot['bYDoctor'],
uID = snapshot.id;
factory UserSearchModel.fromJson(Map<String, dynamic> json) {
return UserSearchModel(
date: json['createdAt'],
firstName: json['firstName'],
lastName: json['lastName'],
phone: json['phone'],
email: json['email'],
searchKey: json['searchKey'],
doctorNote: json['doctorNote'],
bYDoctor: json['bYDoctor'],
uID: json['uID']);
}
Map<String, dynamic> toMap() {
return {
'createdAt': date,
'firstName': firstName,
'lastName': lastName,
'phone': phone,
'email': email,
'searchKey': searchKey,
'doctorNote': doctorNote,
'bYDoctor': bYDoctor,
'uID': uID,
};
}
}
Here is the provider
import 'package:flutter/material.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_model.dart';
import 'package:cespohm/models/user_model.dart';
import 'package:cespohm/services/bywaleed/user_search_new_service.dart';
import 'package:uuid/uuid.dart';
class UserSearchProvider with ChangeNotifier {
final userSearchService = UserSearchService();
final userModel = UserModel();
DateTime _date;
String _firstName;
String _lastName;
String _phone;
String _email;
String _searchKey;
String _doctorNote;
String _bYDoctor;
String _uID;
var uuid = Uuid();
//Getters
DateTime get date => _date;
String get firstName => _firstName;
String get lastName => _lastName;
String get phone => _phone;
String get email => _email;
String get searchKey => _searchKey;
String get doctorNote => _doctorNote;
String get byDoctor => _bYDoctor;
String get uID => _uID;
Stream<List<UserSearchModel>> get users => userSearchService.getEntries();
Stream<List<UserSearchModel>> get userstwo => userSearchService.getEntriesTwo();
//Setters
set changeDate(DateTime date) {
_date = date;
notifyListeners();
}
set changeFirstName(String firstName) {
_firstName = firstName;
notifyListeners();
}
set changeLastName(String lastName) {
_lastName = lastName;
notifyListeners();
}
set changePhone(String phone) {
_phone = phone;
notifyListeners();
}
set changeEmail(String email) {
_email = email;
notifyListeners();
}
set changeSearchKey(String searchKey) {
_searchKey = searchKey;
notifyListeners();
}
set changeDoctorNote(String doctorNote) {
_doctorNote = doctorNote;
notifyListeners();
}
set changeBYDoctor(String bYDoctor) {
_bYDoctor = bYDoctor;
notifyListeners();
}
set changeuID(String uID) {
_uID = uID;
notifyListeners();
}
//Functions
loadAll(UserSearchModel userSearchModel) {
if (userSearchModel != null) {
_date = DateTime.parse(userSearchModel.date);
_firstName = userSearchModel.firstName;
_lastName = userSearchModel.lastName;
_phone = userSearchModel.phone;
_email = userSearchModel.email;
_searchKey = userSearchModel.searchKey;
_doctorNote = userSearchModel.doctorNote;
_bYDoctor = userModel.user.email;
_uID = userSearchModel.uID;
} else {
_date = DateTime.now();
_firstName = null;
_lastName = null;
_phone = null;
_email = null;
_searchKey = null;
_doctorNote = null;
_bYDoctor = null;
_uID = null;
}
}
saveEntry() {
if (_email == null) {
//Add
var newUserModel = UserSearchModel(
date: _date.toIso8601String(),
firstName: _firstName,
lastName: _lastName,
phone: _phone,
email: _email,
searchKey: _searchKey,
doctorNote: _doctorNote,
bYDoctor: _bYDoctor,
uID: _uID);
print(newUserModel.email);
userSearchService.setEntry(newUserModel);
} else {
//Edit
var updatedEntry = UserSearchModel(
date: _date.toIso8601String(),
firstName: _firstName,
lastName: _lastName,
phone: _phone,
email: _email,
searchKey: _searchKey,
doctorNote: _doctorNote,
bYDoctor: _bYDoctor,
uID: _uID);
userSearchService.setEntry(updatedEntry);
}
}
removeEntry(String entryId) {
userSearchService.removeEntry(entryId);
}
}
You need to make sure your User object is initialised. This error appears because user.email is accessed, but your User object is null. Make sure your code considers loading time for the user object if it comes from the API, and has appropriate checks for the case when user == null.
In my app I am taking picture and storing it in the device also storing the path using sqlite . Now if I delete the data using the code given below it delete from the local database(sqlite) but do not delete the picture from the file . for example I create 3 transaction and delete 2 of them but in file I still have 3 images(in file) . So how to delete them . please help .
Saving :
Future _takeImage() async {
PickedFile imageFile = await ImagePicker().getImage(
source: ImageSource.camera,
maxWidth: 600,
);
_saveImg(imageFile);
}
Future _saveImg(PickedFile imageFile) async {
if (imageFile == null) {
return;
}
setState(() {
_storedImage = File(imageFile.path);
});
final appDirectory = await sysPath.getApplicationDocumentsDirectory();
final fileName =
path.basename(_storedImage.path); //name of the photo created by camera.
final savedImage = await _storedImage
.copy('${appDirectory.path}/$fileName'); // storing image.
widget.onSelectImage(savedImage.path);
}
Deleting :
Future<void> deleteExpanse(int id) async {
try {
final MyDatabase dbManager = MyDatabase();
await dbManager.deleteTransaction(id, "Expense");
DataSample temp = _expenseItems.firstWhere((element) => id == element.id);
await _deleteImage(temp);
_expenseItems.removeWhere((element) => element.id == id);
} catch (error) {
throw error;
}
notifyListeners();
}
_deleteImage(DataSample data )async {
final directory = await getApplicationDocumentsDirectory();
final path = join(directory.path, data.image );
bool isExist = await File(path).exists();
if (isExist) {
await File(path).delete();
}
}
for detail code :
taking or choosing picture from here and passing it to a method =>
class ImageInput extends StatefulWidget {
final Function onSelectImage;
final String imageFile;
ImageInput(this.onSelectImage, this.imageFile);
#override
_ImageInputState createState() => _ImageInputState();
}
class _ImageInputState extends State<ImageInput> {
File _storedImage;
Future _choseImage() async {
try {
PickedFile imageFile = await ImagePicker().getImage(
source: ImageSource.gallery,
maxWidth: 600,
);
_saveImg(imageFile);
} catch (error) {
throw error;
}
}
Future _takeImage() async {
try {
PickedFile imageFile = await ImagePicker().getImage(
source: ImageSource.camera,
maxWidth: 600,
);
_saveImg(imageFile);
} catch (error) {
throw error;
}
}
Future _saveImg(PickedFile imageFile) async {
try {
if (imageFile == null) {
return;
}
bool a = await File(imageFile.path).exists();
setState(() {
if(a){
_storedImage = File(imageFile.path);
}
});
final appDirectory = await sysPath.getApplicationDocumentsDirectory();
final fileName = path
.basename(_storedImage.path); //name of the photo created by camera.
final savedImage = await _storedImage
.copy('${appDirectory.path}/$fileName'); // storing image.
widget.onSelectImage(savedImage.path);
} catch (error) {
throw error;
}
}
#override
void initState() {
// TODO: implement initState
_storedImage = widget.imageFile.isEmpty ? null : File(widget.imageFile);
super.initState();
}
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height * 0.24,
width: MediaQuery.of(context).size.width * 0.5,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(width: 1, color: Color(0xFF495464)),
),
child: _storedImage != null
? Image.file(
_storedImage,
fit: BoxFit.cover,
width: double.infinity,
)
: Text(
"No Image Chosen",
style: GoogleFonts.courgette(
fontSize: MediaQuery.of(context).size.width * 0.05),
),
alignment: Alignment.center,
),
SizedBox(width: 10),
Expanded(
child: FittedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FlatButton.icon(
icon: Icon(
Icons.camera,
color: Color(0xFF495464),
),
label: Text(
'Take Picture',
style: TextStyle(color: Color(0xFF495464), fontSize: 20),
),
onPressed: _takeImage,
),
FlatButton.icon(
icon: Icon(
Icons.photo_library,
color: Color(0xFF495464),
),
label: Text(
'Choose Picture',
style: TextStyle(color: Color(0xFF495464), fontSize: 20),
),
onPressed: _choseImage,
),
],
),
),
),
],
);
}
}
it comes here and then I pass it to the provider delete method given above(I only give the expense delete method above) =>
var _initState = true;
var trData;
String initialPrice = '';
var _newTransaction = DataSample(
id: null,
datetime: null,
title: '',
image: '',
price: 0.0,
description: '',
);
#override
void didChangeDependencies() {
if (_initState) {
trData = ModalRoute.of(context).settings.arguments as Map;
if (trData['id'] != 0) {
if (trData['type'] == "Expense") {
_newTransaction = Provider.of<ExpanseProvider>(context, listen: false)
.findId(trData['id']);
} else {
_newTransaction = Provider.of<IncomeProvider>(context, listen: false)
.findId(trData['id']);
}
_selectedDate = _newTransaction.datetime;
initialPrice = _newTransaction.price.toString();
}
}
_initState = false;
super.didChangeDependencies();
}
final _gKey = GlobalKey<FormState>();
DateTime _selectedDate = DateTime.now();
String _pickedImage = '';
void _selectImage(String pickedImage) {
_pickedImage = pickedImage;
}
void _saveInput(String page) {
final _isValid = _gKey.currentState.validate();
if (!_isValid) {
return;
}
_gKey.currentState.save();
_newTransaction = DataSample(
title: _newTransaction.title,
datetime: _selectedDate,
image: _pickedImage.isEmpty ? _newTransaction.image : _pickedImage,
id: _newTransaction.id,
price: _newTransaction.price,
description: _newTransaction.description,
);
deleteOrUpdate(page);
}
Future<void> deleteOrUpdate(String page) async {
if (_newTransaction.id == null) {
if (page == 'Expense') {
await Provider.of<ExpanseProvider>(context, listen: false)
.addExpanseTransaction(_newTransaction)
.then((value) => Navigator.of(context).pop())
.catchError((error) {
return _onError();
});
} else {
await Provider.of<IncomeProvider>(context, listen: false)
.addIncomeTransaction(_newTransaction)
.then((value) => Navigator.of(context).pop())
.catchError((error) {
return _onError();
});
}
} else {
if (page == 'Expense') {
await Provider.of<ExpanseProvider>(context, listen: false)
.updateExpense(_newTransaction)
.then((value) => Navigator.of(context).pop())
.catchError((error) {
return _onError();
});
} else {
await Provider.of<IncomeProvider>(context, listen: false)
.updateIncome(_newTransaction)
.then((value) => Navigator.of(context).pop())
.catchError((error) {
return _onError();
});
}
}
}
I once faced a similar issue using the async file deletion, I then switched to the deleteSync method. I guess in your case it should have no side effect since you already await for deletion.
Documentation: https://api.flutter.dev/flutter/dart-io/FileSystemEntity/deleteSync.html
I started learning Flutter. I am developing a simple application using it. Now, I am developing a feature where my application will display the records from the SQLite database and where the user adds the new records into the SQLite database. But my ListView is displaying the blank screen.
I have a class called DatabaseHelper with the following code.
class DatabaseHelper {
static DatabaseHelper _databaseHelper;
Database _database;
String noteTable = 'note_table';
String colId = 'id';
String colTitle = 'title';
String colDescription = 'description';
String colPriority = 'priority';
String colDate = 'date';
DatabaseHelper._createInstance();
factory DatabaseHelper() {
if (_databaseHelper == null) {
_databaseHelper = DatabaseHelper._createInstance();
}
return _databaseHelper;
}
Future<Database> get database async {
if (_database == null) {
_database = await initializeDatabase();
}
return _database;
}
Future<Database> initializeDatabase() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'notes.db';
var notesDatabase = await openDatabase(path, version: 1, onCreate: _createDB);
return notesDatabase;
}
void _createDB(Database db, int newVersion) async {
await db.execute('CREATE TABLE $noteTable($colId INTEGER PRIMARY KEY AUTOINCREMENT, $colTitle TEXT, $colDescription TEXT, $colPriority INTEGER, $colDate TEXT)');
}
Future<List<Map<String, dynamic>>> getNoteMapList() async {
Database db = await this.database;
return await db.query(noteTable, orderBy: '$colPriority ASC');
}
Future<int> insertNote(Note note) async {
Database db = await this.database;
return await db.insert(noteTable, note.toMap());
}
Future<int> updateNote(Note note) async {
var db = await this.database;
return await db.update(noteTable, note.toMap(), where: '$colId = ?', whereArgs: [note.id]);
}
Future<int> deleteNote(int id) async {
var db = await this.database;
return await db.rawDelete('DELETE FROM $noteTable WHERE $colId = $id');
}
Future<int> getCount() async {
Database db = await this.database;
List<Map<String, dynamic>> x = await db.rawQuery('SELECT COUNT(*) FROM $noteTable');
return Sqflite.firstIntValue(x);
}
}
Then I have a widget called NoteList with the following code where the list of items are displayed.
class NoteList extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _NoteListState();
}
}
class _NoteListState extends State<NoteList> {
List<Note> _notes = [];
int _count = 0;
DatabaseHelper _databaseHelper = DatabaseHelper();
_NoteListState() {
this._notes = getNotes();
this._count = _notes.length;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Notes"),),
body: Container(
child: getListView(context),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
navigateToNoteForm("Add Note");
},
),
);
}
Widget getListView(BuildContext context) {
return ListView.builder(
itemCount: _count,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
backgroundColor: _notes[index].priority == 1? Colors.yellow: Colors.red,
child: Icon(_notes[index].priority == 1 ? Icons.arrow_right : Icons.add),
),
title: Text(_notes[index].title),
subtitle: Text(_notes[index].date),
trailing: Icon(Icons.delete),
onTap: () {
navigateToNoteForm("Edit Note", _notes[index]);
},
);
});
}
void navigateToNoteForm(String pageTitle, [Note note]) async {
bool result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
return NoteForm(pageTitle, note);
}));
if (result) {
setState(() {
debugPrint("Updating list");
_notes = getNotes();
_count = _notes.length;
});
}
}
List<Note> getNotes() {
List<Note> notes = List<Note>();
Future<List<Map<String, dynamic>>> notesFuture = _databaseHelper.getNoteMapList();
notesFuture.then((notesMap) {
debugPrint("Total notes found in the database ${notesMap.length}");
notesMap.forEach((map) {
notes.add(Note.fromMapObject(map));
});
});
return notes;
}
}
Then I also have another widget class called NoteForm with the following code.
class NoteForm extends StatefulWidget {
String _title = "";
Note _note = null;
NoteForm(String title, [Note note]) {
this._title = title;
this._note = note;
}
#override
State<StatefulWidget> createState() {
return _NoteFormState();
}
}
class _NoteFormState extends State<NoteForm> {
double _minimumPadding = 15.0;
var _priorities = [ 1, 2 ];
var _titleController = TextEditingController();
var _descriptionController = TextEditingController();
var _dateController = TextEditingController();
DatabaseHelper _databaseHelper = DatabaseHelper();
var _selectedPriority = 1;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget._title),),
body: Builder(
builder: (scaffoldContext) => Form(
child: Column(
children: <Widget>[
Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding),
child: TextFormField(
controller: _titleController,
decoration: InputDecoration(
labelText: "Title",
hintText: "Enter title"
),
),
),
),
Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding),
child: TextFormField(
controller: _descriptionController,
decoration: InputDecoration(
labelText: "Description",
hintText: "Enter description"
),
),
)
),
Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding),
child: TextFormField(
controller: _dateController,
decoration: InputDecoration(
labelText: "Date",
hintText: "Enter date"
),
),
),
),
Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding),
child: DropdownButton<int>(
value: _selectedPriority,
items: _priorities.map((dropdownItem) {
return DropdownMenuItem<int>(
value: dropdownItem,
child: Text(dropdownItem == 1? "Low": "High"),
);
}).toList(),
onChanged: (int newSelectedValue) {
setState(() {
_selectedPriority = newSelectedValue;
});
},
),
),
),
Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding),
child: RaisedButton(
child: Text(
"Save"
),
onPressed: () {
_save(scaffoldContext);
},
),
),
)
],
),
),
)
);
}
void _save(BuildContext context) async {
Note note = Note();
note.title = _titleController.text;
note.description = _descriptionController.text;
note.date = _dateController.text;
note.priority = _selectedPriority;
if (widget._note != null && widget._note.id!=null) {
//update
_databaseHelper.updateNote(note);
this.showSnackBar(context, "Note has been updated.");
} else {
//create
_databaseHelper.insertNote(note);
this.showSnackBar(context, "Note has been added.");
}
closeForm(context);
}
void showSnackBar(BuildContext context, String message) {
var snackBar = SnackBar(
content: Text(message),
action: SnackBarAction(
label: "UNDO",
onPressed: () {
},
),
);
Scaffold.of(context).showSnackBar(snackBar);
}
void closeForm(BuildContext context) {
Navigator.pop(context, true);
}
}
When I run my application, it is just displaying the blank screen as follows.
As you can see I am logging out the number of records returned from the database using debugPrint method. It is saying that there are 6 records within the database. It is just not displaying the records. What is wrong with my code and how can I fix it?
As i mention in comment that was happening because of async task take some time to perform and if you do not keep it async then setState function execute before actual data load or set.
So Following changes solve your issue.
make getNotes async method And
getNotes().then((noteresponce){ setState((){ _notes=noteresponce; _count = _notes.length;} });
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
I am building an application using Flutter and Dart programming language. I am using SQLite as the local database. Now I am developing a feature where I pass the data to a new form page when one of the items of the ListView is tapped. But updating the data is not working.
Following is my database helper class. Pay attention to the updateNote method
class DatabaseHelper {
static DatabaseHelper _databaseHelper;
Database _database;
String noteTable = 'note_table';
String colId = 'id';
String colTitle = 'title';
String colDescription = 'description';
String colPriority = 'priority';
String colDate = 'date';
DatabaseHelper._createInstance();
factory DatabaseHelper() {
if (_databaseHelper == null) {
_databaseHelper = DatabaseHelper._createInstance();
}
return _databaseHelper;
}
Future<Database> get database async {
if (_database == null) {
_database = await initializeDatabase();
}
return _database;
}
Future<Database> initializeDatabase() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'notes.db';
var notesDatabase = await openDatabase(path, version: 1, onCreate: _createDB);
return notesDatabase;
}
void _createDB(Database db, int newVersion) async {
await db.execute('CREATE TABLE $noteTable($colId INTEGER PRIMARY KEY AUTOINCREMENT, $colTitle TEXT, $colDescription TEXT, $colPriority INTEGER, $colDate TEXT)');
}
Future<List<Map<String, dynamic>>> getNoteMapList() async {
Database db = await this.database;
return await db.query(noteTable, orderBy: '$colPriority ASC');
}
Future<int> updateNote(Note note) async {
var db = await this.database;
return await db.update(noteTable, note.toMap(), where: '$colId = ?', whereArgs: [note.id]);
}
//there are other methods hidden here
}
I have a widget class that displays the ListView with the following code
class NoteList extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _NoteListState();
}
}
class _NoteListState extends State<NoteList> {
List<Note> _notes = [];
int _count = 0;
DatabaseHelper _databaseHelper = DatabaseHelper();
_NoteListState() {
getNotes();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Notes"),),
body: Container(
child: getListView(context),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
navigateToNoteForm("Add Note");
},
),
);
}
Widget getListView(BuildContext context) {
return ListView.builder(
itemCount: _count,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
backgroundColor: _notes[index].priority == 1? Colors.yellow: Colors.red,
child: Icon(_notes[index].priority == 1 ? Icons.arrow_right : Icons.add),
),
title: Text(_notes[index].title),
subtitle: Text(_notes[index].date),
trailing: Icon(Icons.delete),
onTap: () {
navigateToNoteForm("Edit Note", _notes[index]);
},
);
});
}
void navigateToNoteForm(String pageTitle, [Note note]) async {
bool result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
return NoteForm(pageTitle, note);
}));
if (result) {
getNotes();
}
}
void getNotes() {
List<Note> notes = List<Note>();
Future<List<Map<String, dynamic>>> notesFuture = _databaseHelper.getNoteMapList();
notesFuture.then((notesMap) {
debugPrint("Total notes found in the database ${notesMap.length}");
notesMap.forEach((map) {
notes.add(Note.fromMapObject(map));
});
setState(() {
_notes = notes;
_count = notes.length;
});
});
}
}
As you can see I am passing the data to the next widget, NoteForm when one of the ListView's items is clicked.
This is my NoteForm class's implementation.
class NoteForm extends StatefulWidget {
String _title = "";
Note _note = null;
NoteForm(String title, [Note note]) {
this._title = title;
this._note = note;
}
#override
State<StatefulWidget> createState() {
return _NoteFormState(this._title, this._note);
}
}
class _NoteFormState extends State<NoteForm> {
double _minimumPadding = 15.0;
var _priorities = [ 1, 2 ];
var _titleController = TextEditingController();
var _descriptionController = TextEditingController();
var _dateController = TextEditingController();
DatabaseHelper _databaseHelper = DatabaseHelper();
var _selectedPriority = 1;
String _title;
Note _note;
_NoteFormState(String title, [Note note]) {
this._title = title;
this._note = note;
if (this._note != null && this._note.id != null) {
_titleController.text = this._note.title;
_descriptionController.text = this._note.description;
_dateController.text = this._note.date;
_selectedPriority = this._note.priority;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(this._title),),
body: Builder(
builder: (scaffoldContext) => Form(
child: Column(
children: <Widget>[
Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding),
child: TextFormField(
controller: _titleController,
decoration: InputDecoration(
labelText: "Title",
hintText: "Enter title"
),
),
),
),
Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding),
child: TextFormField(
controller: _descriptionController,
decoration: InputDecoration(
labelText: "Description",
hintText: "Enter description"
),
),
)
),
Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding),
child: TextFormField(
controller: _dateController,
decoration: InputDecoration(
labelText: "Date",
hintText: "Enter date"
),
),
),
),
Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding),
child: DropdownButton<int>(
value: _selectedPriority,
items: _priorities.map((dropdownItem) {
return DropdownMenuItem<int>(
value: dropdownItem,
child: Text(dropdownItem == 1? "Low": "High"),
);
}).toList(),
onChanged: (int newSelectedValue) {
setState(() {
_selectedPriority = newSelectedValue;
});
},
),
),
),
Container(
child: Padding(
padding: EdgeInsets.all(_minimumPadding),
child: RaisedButton(
child: Text(
"Save"
),
onPressed: () {
_save(scaffoldContext);
},
),
),
)
],
),
),
)
);
}
void _save(BuildContext context) async {
Note note = Note();
note.title = _titleController.text;
note.description = _descriptionController.text;
note.date = _dateController.text;
note.priority = _selectedPriority;
if (widget._note != null && widget._note.id!=null) {
//update
_databaseHelper.updateNote(note);
debugPrint("Note title is ${note.title}");
debugPrint("Note description is ${note.description}");
debugPrint("Note date is ${note.date}");
debugPrint("Note priority is ${note.priority}");
debugPrint("Note has been updated.");
this.showSnackBar(context, "Note has been updated.");
} else {
//create
_databaseHelper.insertNote(note);
debugPrint("Note has been created.");
this.showSnackBar(context, "Note has been added.");
}
closeForm(context);
}
void showSnackBar(BuildContext context, String message) {
var snackBar = SnackBar(
content: Text(message),
action: SnackBarAction(
label: "UNDO",
onPressed: () {
},
),
);
Scaffold.of(context).showSnackBar(snackBar);
}
void closeForm(BuildContext context) {
Navigator.pop(context, true);
}
}
When I update the note, it does not update it. The list view is still displaying the same data. What is wrong with my code and how can I fix it?
When you are making your call to _databaseHelper.updateNote(note); I believe it you should be awaiting this so await _databaseHelper.updateNote(note); because it returns a future so right now you are not waiting for your data to return. You have a couple other functions that return futures too.
maybe you need a await or future then, where you call
also give a print in the _databaseHelper.updateNote
you can see which one comes first
void _save(BuildContext context) async {
Note note = Note();
note.title = _titleController.text;
note.description = _descriptionController.text;
note.date = _dateController.text;
note.priority = _selectedPriority;
if (widget._note != null && widget._note.id!=null) {
//update
await _databaseHelper.updateNote(note);
debugPrint("Note title is ${note.title}");
debugPrint("Note description is ${note.description}");
debugPrint("Note date is ${note.date}");
debugPrint("Note priority is ${note.priority}");
debugPrint("Note has been updated.");
this.showSnackBar(context, "Note has been updated.");
} else {
//create
await _databaseHelper.insertNote(note);
debugPrint("Note has been created.");
this.showSnackBar(context, "Note has been added.");
}
closeForm(context);
}
I found the issue. It is with my code. Have a look at the following snippet.
Note note = Note();
note.title = _titleController.text;
note.description = _descriptionController.text;
note.date = _dateController.text;
note.priority = _selectedPriority;
I am not assigning the id value. So that id is always null for updating. Thanks for all your help.