I'm trying to update a variable value inside a void method in flutter, tried using StatefulBuilder but the value does not get changed.
Here is my code:
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final user = FirebaseAuth.instance.currentUser;
String type = "";
void checkUser() async {
await FirebaseFirestore.instance
.collection('users')
.doc(user!.uid)
.get()
.then(
(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
var data = documentSnapshot.data();
var res = data as Map<String, dynamic>;
if (res["type"] == "Salarié") {
print('Salarié');
} else if (res["type"] == "Auto entrepreneur") {
print('Auto entrrepreneu');
} else {
showDialog<String>(
context: context,
builder: (_) => StatefulBuilder(
builder: (modalContext, modalSetState) => AlertDialog(
title: const Text('Choissisez votre type'),
content: const Text('Choisir votre type de user'),
actions: <Widget>[
TextButton(
onPressed: () {
setState(() {
type = "Salarié";
});
Navigator.pop(context, 'Cancel');
},
child: const Text('Salarié'),
),
TextButton(
onPressed: () {
setState(() {
type = "Auto entrepreneur";
});
Navigator.pop(context, 'OK');
},
child: const Text('Auto-entrepreneur'),
),
],
),
));
return FirebaseFirestore.instance
.collection("users")
.doc(user!.uid)
.update({
"type": type,
});
}
} else {}
},
);
}
#override
void initState() {
super.initState();
checkUser();
}
#override
Widget build(BuildContext context) {
return Scaffold(...);
It's all inside a StatefulWidget though, I'm not sure whether this is the correct way to do this because the value does not get changed! I appreciate your help.
edit: I'm calling this method inside the initState()
showDialog is not a synchronous operation and the code basically sends the dialog and immediately continues. to fix that, I had to add async and await, and it worked like a charm.
Related
I have an asynchronous function for checking internet access which is in another file. Here is its code:
//Check User Connection
class CheckUserConnection {
Future checkInternetAvailability() async {
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
return true;
}
} on SocketException catch (_) {
return false;
}
}
}
I need it to be activated when a button is pressed in the main menu. Here is the code for this screen:
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Example';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Container(
child: Scaffold(
appBar: AppBar(title: const Text(_title)),
// body: const MyStatelessWidget(),
body: const MainWidget(),
),
)
);
}
}
// class MyStatelessWidget extends StatelessWidget {
// const MyStatelessWidget({Key? key}) : super(key: key);
class MainWidget extends StatefulWidget {
const MainWidget({Key? key}) : super(key: key);
#override
State<MainWidget> createState() => _MainWidgetState();
}
class _MainWidgetState extends State<MainWidget> {
#override
Widget build(BuildContext context) {
InternetDialogHandler _internetDialogHandler = InternetDialogHandler();
CheckUserConnection _checkUserConnection = CheckUserConnection();
bool _internetAvailable = await _checkUserConnection.checkInternetAvailability();
// bool _internetAvailable = false;
return Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: [
GradientButton(label: 'New Game', onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const NewGameRoute()),
);
}),
GradientButton(label: 'Continue Game', onTap: () {
if(_internetAvailable)
{
//do something here;
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const NewGameRoute()),
);
} else{
//handle no internet here
_internetDialogHandler.showInternetDialog(context);
}
}),
],
),
Column(
children: [
GradientButton(label: 'Back Button', onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const BackRoute()),
);
// print('Button 1');
}),
GradientButton(label: 'Button 2', onTap: () {print('Button 2');}),
GradientButton(label: 'Internet', onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const InternetRoute()),
);
}),
],
)
],
),
);
}
}
But the problem is that when I paste:
bool _internetAvailable = await _checkUserConnection.checkInternetAvailability();
I get an error:
The await expression can only be used in an async function.
Why? Async is already in that function, which is in another file. Where else do I need to add async in my main page code?
I've been given advice:
In initstate just call a method. And in that method add async and check internrt and set state based on internet availability
But since I'm a beginner, I don't understand what exactly I should do.
Edit1. This code does not throw any errors:
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Example';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Container(
child: Scaffold(
appBar: AppBar(title: const Text(_title)),
// body: const MyStatelessWidget(),
body: const MainWidget(),
),
)
);
}
}
// class MyStatelessWidget extends StatelessWidget {
// const MyStatelessWidget({Key? key}) : super(key: key);
class MainWidget extends StatefulWidget {
const MainWidget({Key? key}) : super(key: key);
#override
State<MainWidget> createState() => _MainWidgetState();
}
class _MainWidgetState extends State<MainWidget> {
CheckUserConnection _checkUserConnection = CheckUserConnection();
bool? _internetAvailable;
void checkNet() async{
_internetAvailable = await
_checkUserConnection.checkInternetAvailability();
// can you do any async operation into this method, Just be careful to check it
}
#override
void initState(){
super.initState();
checkNet();
}
#override
Widget build(BuildContext context) {
InternetDialogHandler _internetDialogHandler = InternetDialogHandler();
// CheckUserConnection _checkUserConnection = CheckUserConnection();
// bool _internetAvailable = await _checkUserConnection.checkInternetAvailability();
// bool _internetAvailable = false;
return Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: [
GradientButton(label: 'New Game', onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const NewGameRoute()),
);
}),
GradientButton(label: 'Continue Game', onTap: () {
// if(_internetAvailable)
// {
// //do something here;
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => const NewGameRoute()),
// );
// } else{
// //handle no internet here
// _internetDialogHandler.showInternetDialog(context);
// }
return _internetAvailable == null?
_internetDialogHandler.showInternetDialog(context)
:
print('_internetAvailable = null');
}),
],
),
Column(
children: [
GradientButton(label: 'Back Button', onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const BackRoute()),
);
// print('Button 1');
}),
GradientButton(label: 'Button 2', onTap: () {print('Button 2');}),
GradientButton(label: 'Internet', onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const InternetRoute()),
);
}),
],
)
],
),
);
}
}
But the problem is that _internetAvailable returns null regardless of the Internet connection.
Edi2. Trying Kaushik Chandru's code:
class _MainWidgetState extends State<MainWidget> {
InternetDialogHandler _internetDialogHandler = InternetDialogHandler();
CheckUserConnection _checkUserConnection = CheckUserConnection();
#override
void initState(){
super.initState();
checkInternet((){
//Add what to do if internet is available
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const NewGameRoute()),
);
},
(){
//Add what to do if no internet
_internetDialogHandler.showInternetDialog(context);
}
);
}
checkInternet(Function? ifAvailable, Function? ifUnavailable) async{
bool internetAvailable = await _checkUserConnection.checkInternetAvailability();
if(internetAvailable)
{
ifAvailable();
}
else{
ifUnavailable();
}
}
#override
Widget build(BuildContext context) {
...
I have this error:
88:18: Error: Can't use an expression of type 'Function?' as a function because it's potentially null.
- 'Function' is from 'dart:core'.
Try calling using ?.call instead.
ifAvailable();
^
lib/main.dart:91:20: Error: Can't use an expression of type 'Function?' as a function because it's potentially null.
- 'Function' is from 'dart:core'.
Try calling using ?.call instead.
ifUnavailable();
^
Edit3. For Kaushik Chandru only
lib/main.dart:92:21: Error: Expected an identifier, but got ';'.
Try inserting an identifier before ';'.
ifAvailable()?;
^
lib/main.dart:92:21: Error: Expected ':' before this.
ifAvailable()?;
^
lib/main.dart:95:23: Error: Expected an identifier, but got ';'.
Try inserting an identifier before ';'.
ifUnavailable()?;
^
lib/main.dart:95:23: Error: Expected ':' before this.
ifUnavailable()?;
^
lib/main.dart:92:18: Error: Can't use an expression of type 'Function?' as a function because it's potentially null.
- 'Function' is from 'dart:core'.
Try calling using ?.call instead.
ifAvailable()?;
^
lib/main.dart:95:20: Error: Can't use an expression of type 'Function?' as a function because it's potentially null.
- 'Function' is from 'dart:core'.
Try calling using ?.call instead.
ifUnavailable()?;
^
Edit4 for Hossein Asadi. Just in case, I added the print in different places, tried different options. But there is no such entry in the console. When I click on my button, the condition that _internetAvailable == null is triggered.
class _MainWidgetState extends State<MainWidget> {
CheckUserConnection _checkUserConnection = CheckUserConnection();
bool? _internetAvailable;
void checkNet() async{
_internetAvailable = await
_checkUserConnection.checkInternetAvailability();
print("ok");
setState((){});
// can you do any async operation into this method, Just be careful to check it
}
#override
void initState(){
super.initState();
checkNet();
print("ok");
}
Add an initstate to the stateful widget. Inside the initstate add this code
checkInternet((){
//Add what to do if internet is available
},
(){
//Add what to do if no internet
}
);
Then define a function
checkInternet(VoidCallback ifAvailable, VoidCallback ifUnavailable) async{
bool internetAvaibale = await _checkInternetConnection.checkInternetAvailability();
if(internetAvailable)
{
ifAvailable();
}
else{
ifUnavailable();
}
}
you called bool _internetAvailable = await _checkUserConnection.checkInternetAvailability(); in build method, In the event that build method can't be async.
you must be do like below code:
class _MainWidgetState extends State<MainWidget> {
bool? _internetAvailable;
void checkNet() async{
_internetAvailable = await
_checkUserConnection.checkInternetAvailability();
setState((){});
// can you do any async operation into this method, Just be careful to check it
}
#overrider
void initState(){
super.initState();
checkNet();
}
#override
Widget build(BuildContext context) {
....
return _internetAvailable == null?
CupertinoActivityIndicator()//loading
:Center(
child: Column(
....
On a page, I have a button. I am using the OnTap to display a bottom sheet. But the widget does not display on the screen. I don't know why. I have tried different option and even tried using other widget to check if they were displaying properly. It was working. But it is not with the bottom sheet. If you can explain to me what I am missing, it would be appreciated. Thank you.
I have tried also this, see code below, but it is not working properly because I can not use setstate.
ERROR : lost connection. It is killing the simulator.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:permission_handler/permission_handler.dart';
class Voice_Recording extends StatefulWidget {
const Voice_Recording({Key key}) : super(key: key);
#override
_Voice_RecordingState createState() => _Voice_RecordingState();
}
class _Voice_RecordingState extends State<Voice_Recording> {
final recorder = FlutterSoundRecorder();
#override
void initState(){
super.initState();
initRecorder();
}
#override
void dispose(){
recorder.closeRecorder();
super.dispose();
}
Future initRecorder() async{
final status = await Permission.microphone.request();
if(status != PermissionStatus.granted){
throw 'Microphone permission not granted!';
}
await recorder.openRecorder();
recorder.setSubscriptionDuration(Duration(milliseconds: 500 ));
}
Future record() async{
await recorder.startRecorder(toFile:'audio');
}
Future stop() async{
final path = await recorder.stopRecorder();
final audioFile = File(path);
print('recorded audio File :$audioFile');
}
#override
Widget build(BuildContext context) {
return BottomSheet(
builder: (context){
return Column(
children: [
StreamBuilder<RecordingDisposition>(
stream: recorder.onProgress,
builder: (context,snapshot){
final duration=snapshot.hasData?
snapshot.data.duration: Duration.zero;
String twoDigits(int n) => n.toString().padLeft(60);
final twoDigitsMinutes= twoDigits(duration.inMinutes.remainder(60));
final twoDigitsSeconds = twoDigits(duration.inSeconds.remainder(60));
return Text('$twoDigitsMinutes:$twoDigitsSeconds',
style: TextStyle(
fontSize: 80,
fontWeight: FontWeight.bold,
),);
},
),
Center(child:
ElevatedButton(child:Icon(recorder.isRecording? Icons.stop:Icons.mic,size:80),
onPressed: () async{
if(recorder.isRecording){
await stop();
}else{
await record();
}
setState(() {
});
},)
),
]);
});
}
}
New code. not working perfectly due to lost of SetState()
showDialogVoiceRecording(BuildContext context, {String myText = 'Record and speak !'}) {
showModalBottomSheet<void>(
context: context,
isDismissible: false,
builder: (BuildContext context) {
context = context;
return Column(
children: [
StreamBuilder<RecordingDisposition>(
stream: recorder.onProgress,
builder: (context,snapshot){
final duration=snapshot.hasData?
snapshot.data.duration: Duration.zero;
String twoDigits(int n) => n.toString().padLeft(60);
final twoDigitsMinutes= twoDigits(duration.inMinutes.remainder(60));
final twoDigitsSeconds = twoDigits(duration.inSeconds.remainder(60));
return Text('$twoDigitsMinutes:$twoDigitsSeconds',
style: TextStyle(
fontSize: 80,
fontWeight: FontWeight.bold,
),);
},
),
Center(child:
ElevatedButton(child:Icon(recorder.isRecording? Icons.stop:Icons.mic,size:80),
onPressed: () async{
if(recorder.isRecording){
await stop();
}else{
await record();
}
/* setState(() {
});*/
},)
),
],
);
}
);
}
I was making a simple cart app, it did well but cart count not showing when app is closed and reopened again.
I am using provider and calls fetchCartProducts() method when the app is opened. It calls fine. but cart badge widget itemcount is not changing at first time. only shows 0 at first time.
Future<void> fetchCartProducts() async {
final dataList = await DBHelper.getData('cart_food');
//convert dataList to _cartItems
final entries = dataList
.map((item) => CartModel(
item['id'],
item['price'].toDouble(),
item['productName'],
item['quantity'],
))
.map((cart) => MapEntry(cart.id, cart));
_cartItems = Map<String, CartModel>.fromEntries(entries);
print('inside fetchcart');
}
class HomeScreen extends StatefulWidget
{
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen>
{
Future<List<FoodItem>> _foodItems;
var _isInit = true;
#override
void initState() {
super.initState();
_foodItems = ApiService.getFoodItems();
Provider.of<CartProvider>(context, listen: false).fetchCartProducts();
setState(() {});
}
#override
void didChangeDependencies()
{
if (_isInit) {
Provider.of<CartProvider>(context).fetchCartProducts();
_isInit = false;
setState(() {});
}
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
final cart = Provider.of<CartProvider>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: const Text('Food Cart'),
actions: [
//this is not updating when the app is closed and opened again.
Consumer<CartProvider>(
builder: (_, cartprovider, ch) => Badge(
child: ch,
value: cartprovider.itemCount.toString(),
),
child: IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) {
return CartScreen();
}),
);
},
),
),
],
),
body: FutureBuilder<List<FoodItem>>(
future: _foodItems,
builder: (conext, snapshot) => !snapshot.hasData
? const Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
FoodItem foodItem = snapshot.data[index];
return ListTile(
title: Text(foodItem.productName),
subtitle: Text(foodItem.variant),
trailing: IconButton(
onPressed: () {
cart.addToCart(
foodItem.storeid.toString(),
foodItem.productName,
1,
foodItem.price,
);
setState(() {});
},
icon: const Icon(Icons.shopping_cart),
),
);
},
),
),
);
}
}
otherwise when item added to cart, it working fine. the data loss when reopened. how to get total count when the app starts?
In order to rebuild Consumer you need to call notifyListeners() inside your CartProvider
Add notifyListeners() to your fetchCartProducts() after assigning the value to _cartItems = Map<String, CartModel>.fromEntries(entries);
Future<void> fetchCartProducts() async {
final dataList = await DBHelper.getData('cart_food');
//convert dataList to _cartItems
final entries = dataList
.map((item) => CartModel(
item['id'],
item['price'].toDouble(),
item['productName'],
item['quantity'],
))
.map((cart) => MapEntry(cart.id, cart));
_cartItems = Map<String, CartModel>.fromEntries(entries);
notifyListeners(); // <------- this line
print('inside fetchcart');
}
i just need when the user open the screen the notification icon button change when he click, it's value is coming from shared preferences. the problem is the icon is never changed!
the initState code:
#override
void initState() {
super.initState();
_isActiveNotification = _notificationGetState();
}
_notificationGetState function is:
//getting notification on/off
Future<bool> _notificationGetState() async {
final SharedPreferences _prefs = await SharedPreferences.getInstance();
return _prefs.getBool('notification') ?? true;
}
_isActiveNotification variable is:
late Future<bool> _isActiveNotification;
the class of the notification icon button is:
class _NoificationActivationButton extends StatefulWidget {
_NoificationActivationButton();
#override
_NoificationActivationButtonState createState() =>
_NoificationActivationButtonState();
}
class _NoificationActivationButtonState
extends State<_NoificationActivationButton> {
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
//function haveing the return value
future: _isActiveNotification,
builder: (context, snapshot) {
bool data = snapshot.data!;
return IconButton(
icon: Icon(
data
? Icons.notifications_active_outlined
: Icons.notifications_off_outlined,
color: Colors.white,
size: 40,
),
onPressed: () {
setState(() {
data = !data;
});
},
);
});
}
just call setstate
onPressed: () {
data = !data;
// just call setstate((){});
},
Make data a global state.
NOTE: I'm only assuming that you will only call _notificationGetState once (in initState).
Sample...
class _NoificationActivationButtonState
extends State<_NoificationActivationButton> {
final bool _isOtherVersion = true;
late Future<bool> _isActiveNotification;
bool? _data;
#override
void initState() {
super.initState();
_isActiveNotification = _notificationGetState();
}
//getting notification on/off
Future<bool> _notificationGetState() async {
final SharedPreferences _prefs = await SharedPreferences.getInstance();
return _isOtherVersion
? _prefs.getBool('notification') ?? true
: _data = _prefs.getBool('notification') ?? true;
}
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
//function haveing the return value
future: _isActiveNotification,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const CircularProgressIndicator();
}
if (_isOtherVersion && _data == null) {
_data = snapshot.data;
}
return IconButton(
icon: Icon(
_data!
? Icons.notifications_active_outlined
: Icons.notifications_off_outlined,
color: Colors.white,
size: 40,
),
onPressed: () => setState(() => _data = !_data!),
);
},
);
}
}
I a trying to pass a Value from when a button is clicked, What I want in the code is to pass the value from the Button Widget to another Page's Variable named pdfNo. Here's my code:
FlatButton(
padding: EdgeInsets.fromLTRB(2, 5, 5, 5),
child: ListTile(
leading: Icon(Icons.book, color: Color(0xFFEB3549)),
title: Text('Book5'),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFPage() ));
print('Pressed 6');
},
),`
This is the Button on the First Page , On Pressed I would like to pass a Value to a function which is child, so I have created a variable , but cant figure out how to go about this next:
var pdfNo = 2;
bool _isLoading = true;
PDFDocument document;
#override
void initState() {
super.initState();
changePDF(pdfNo);
loadDocument();
}
changePDF(value) async {
setState(() => _isLoading = true);
if (value == 1) {
document = await PDFDocument.fromAsset('assets/sample2.pdf');
} else if (value == 2) {
document = await PDFDocument.fromURL(
"http://conorlastowka.com/book/CitationNeededBook-Sample.pdf");
} else {
document = await PDFDocument.fromAsset('assets/sample.pdf');
}
setState(() => _isLoading = false);
}
So, I would like to pass a int from the button on page 1 Staleful Class to a Stateful Class on second page to changePDF(here).
Please help me out. PS New to Flutter,Dart
You can create a to arguments...
Something like this
class LoginWidgetArguments {
final String username;
LoginWidgetArguments(
{this.username});
}
class LoginWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => _LoginWidgetState();
}
class _LoginWidgetState extends State<LoginWidget>{
LoginWidgetArguments args;
#override
void initState() {
//Get context
// I do this because sometimes it doesn't get the context without the FutureDelay... This workaround I used with the first flutter versions, maybe now its not needed
Future.delayed(Duration.zero, () {
args = ModalRoute.of(context).settings.arguments;
if (args != null) {
print(args.username)
}
});
}
....
}
And to navigate
Navigator.pushNamed(context, LoginPage.routeName,
arguments: LoginWidgetArguments(
user: "user#yay.com");
Edit
Maybe it could be simple...
FlatButton(
padding: EdgeInsets.fromLTRB(2, 5, 5, 5),
child: ListTile(
leading: Icon(Icons.book, color: Color(0xFFEB3549)),
title: Text('Book5'),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFPage(pageNumber: 6) ));
print('Pressed 6');
},
)
class PDFPage extends StatefulWidget {
final int pageNumber;
PDFPage({this.pageNumber});
#override
_PDFPageStage createState() => _PDFPageStage();
}
class _PDFPageStage extends State<PDFPage> {
...
#override
void initState() {
super.initState();
changePDF(widget.pageNumber);
loadDocument();
}
...
}
I'm not sure if I understand the problem correctly, but I think you can pass the number to the constructor of the StatefulWidget. I changed the PDFDocument to a String for simplicity.
Some button press on first page:
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFPage(pdfNo: 4),
),
);
},
The second page:
class PDFPage extends StatefulWidget {
final int pdfNo;
const PDFPage({Key key, this.pdfNo}) : super(key: key);
#override
_PDFPageState createState() => _PDFPageState();
}
class _PDFPageState extends State<PDFPage> {
bool _isLoading = false;
String _document;
void changePDF(int value) async {
setState(() => _isLoading = true);
if (value == 1) {
_document = await Future.delayed(Duration(seconds: 1), () => 'Value 1');
} else if (value == 2) {
_document = await Future.delayed(Duration(seconds: 1), () => 'Value 2');
} else {
_document = await Future.delayed(Duration(seconds: 1), () => 'Other value');
}
setState(() => _isLoading = false);
}
#override
void initState() {
super.initState();
changePDF(widget.pdfNo);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Test'),),
body: Center(
child: _isLoading ? CircularProgressIndicator() : Text(_document ?? '(null)'),
),
);
}
}