how to pass argument to a final variable in flutter - flutter

I have the following code and could not figure out what is the problem.
I want to pass the Bluetooth device to the container in the class DeviceData and this class returns the container. I am beginner in Flutter and when I read the documentation could not understand what they are talking about.
But the first problem is
Can't define the 'const' constructor because the field 'uppersection' is initialized with a non-constant value. Try initializing the field to a constant value, or removing the keyword 'const' from the
and then device cannot be accessed:
The instance member 'device' can't be accessed in an initializer. Try replacing the reference to the instance member with a different expression
class DeviceData extends StatelessWidget {
const DeviceData({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
final uppersection = new Container(
child: Row(
children: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) => ListTile(
leading: (snapshot.data == BluetoothDeviceState.connected)
? Icon(Icons.bluetooth_connected)
: Icon(Icons.bluetooth_disabled),
title: Text('Device is ${snapshot.data.toString().split('.')[0]}.'),
subtitle: Text('${device.id}'),
trailing: StreamBuilder<bool>(
//The below stream is to show either a refresh button or
//CircularProgress based on the service discovering status,
//and a IndexedStack widget is used to present the needed widget
stream: device.isDiscoveringServices,
initialData: false,
builder: (c, snapshot) => IndexedStack(
index: snapshot.data ? 0 : 0,
children: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () => device.discoverServices(),
),
IconButton(
icon: SizedBox(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.grey),
),
width: 17.0,
height: 17.0,
),
onPressed: null,
),
],
),
),
),
),
],
),
);
#override
Widget build(BuildContext context) {
return new Container(
child: Column(
children: <Widget>[
uppersection,
],
),
);
}
}

You should rather make upperSection a getter.
Like so:
Container get upperSection{
return Container(
child: Row(
children: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) => ListTile(
leading: (snapshot.data == BluetoothDeviceState.connected)
? Icon(Icons.bluetooth_connected)
: Icon(Icons.bluetooth_disabled),
title: Text('Device is ${snapshot.data.toString().split('.')[0]}.'),
subtitle: Text('${device.id}'),
trailing: StreamBuilder<bool>(
//The below stream is to show either a refresh button or
//CircularProgress based on the service discovering status,
//and a IndexedStack widget is used to present the needed widget
stream: device.isDiscoveringServices,
initialData: false,
builder: (c, snapshot) => IndexedStack(
index: snapshot.data ? 0 : 0,
children: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () => device.discoverServices(),
),
IconButton(
icon: SizedBox(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.grey),
),
width: 17.0,
height: 17.0,
),
onPressed: null,
),
],
),
),
),
),
],
),
);
}

Related

Getting error "The argument type 'JsObject' can't be assigned to the parameter type 'BuildContext'"

I tried to fix it but it didn't work for me. I think I am missing something, it works in older version of Flutter but not in recent one.
I am trying to set (onTap) function on an image to go to the next widget screen.
Here is my code:
import 'dart:js';
import 'package:flutter/material.dart';
import 'package:zar/screen/category.dart';
class TopCard extends StatefulWidget {
const TopCard({Key? key}) : super(key: key);
#override
State<TopCard> createState() => _TopCardState();
}
// TOP CARD CLASS STARTS HERE
class CardItem {
final String urlImage;
final String title;
final String subTitle;
const CardItem({
required this.urlImage,
required this.title,
required this.subTitle,
});
}
// TOP CARD WIDGETS STARTS HERE
Widget topCard({
required CardItem item,
}) =>
Container(
width: 150,
child: Column(
children: [
Expanded(
child: AspectRatio(
aspectRatio: 2 / 2,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Material(
child: Ink.image(
image: NetworkImage(item.urlImage),
child: InkWell(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Categories(
item: item,
),
),
),
),
),
),
),
),
),
const SizedBox(height: 4),
Text(
item.title,
style: const TextStyle(
color: Color(0xff5e35b1),
fontSize: 20,
fontWeight: FontWeight.bold),
),
Text(
item.subTitle,
style: const TextStyle(
color: Colors.redAccent,
),
),
],
),
);
class _TopCardState extends State<TopCard> {
// TOP CARD LIST VIEW STARTS HERE
List<CardItem> items = const [
CardItem(
urlImage:
'https://img.freepik.com/free-vector/pizza-melted-cartoon-illustration-flat-cartoon-style_138676-2876.jpg?size=338&ext=jpg',
title: 'PIZZA',
subTitle: '\$20',
),
CardItem(
urlImage:
'https://img.freepik.com/free-vector/triangle-sandwich-cartoon-icon-illustration_368721-11.jpg?size=338&ext=jpg',
title: 'SANDWICH',
subTitle: '\$7.99',
),
CardItem(
urlImage:
'https://thumbs.dreamstime.com/b/french-fries-icon-flat-vector-related-icon-long-shadow-web-mobile-applications-can-be-used-as-logo-pictogram-icon-90676285.jpg',
title: 'FRIES',
subTitle: '\$2.99',
),
CardItem(
urlImage:
'https://i.pinimg.com/originals/42/de/53/42de53a97cac79ddcaee570c436a10e6.jpg',
title: 'BURGER',
subTitle: '\$5.99',
),
];
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 20),
height: 150,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: 4,
separatorBuilder: (constext, _) => const SizedBox(width: 16),
itemBuilder: (context, index) => topCard(
item: items[index],
),
),
);
}
}
But I get Error in (context)
child: InkWell(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Categories(
item: item,
),
),
),
),
your helper method needs the context
child: InkWell(
onTap: () => Navigator.push(
context, // HERE You need context
MaterialPageRoute(
builder: (context) => Categories(
item: item,
),
),
),
),
So add it as a parameter
// TOP CARD WIDGETS STARTS HERE
Widget topCard({
required CardItem item,
required BuildContext context
}) => ...
and remove
import 'dart:js'; // it gives you another context that you don't need 'JsObject'
Another solution is to move topCard helper method within your state class, so you dont have to pass the context as an argument.
I got the same error. Flutter automatically imported dart.js library, which is giving the error. Go to the top of the file and remove this line:
import 'dart:js';

Is there a way in flutter to provide the Type for Consumer<T> from parameter?

My problem in summary: I have an app, that has 3-4 master data lists which are stored in SQLite. These list are identical so I could reuse most of the widgets that build up the different master data screens. The problem is that the data is coming from different providers and I cannot find the way to extract these widgets.
This is the part where I construct the list of FishTypes using the Consumer. Can I reuse this somehow for DogTypes, CatTypes etc. without copying the whole code?
Expanded(
child: Container(
child: FutureBuilder(
future: _items,
builder: (ctx, snapshot) =>
snapshot.connectionState == ConnectionState.waiting
? Center(
child: CircularProgressIndicator(),
)
: Consumer<FishTypes>(
builder: (ctx, fishType, _) => ListView.builder(
itemCount: fishType.items.length,
itemBuilder: (ctx, i) =>
ChangeNotifierProvider.value(
value: fishType.items[i],
child: MasterDataMenuItem(),
),
),
),
),
),
),
The MasterDataMenuItem widget is basically a ListTile, where I also use Consumer to rebuild the widget when Favorite or Active switch is toggled. This is again a widget that I'd like to reuse with other Consumer types.
class MasterDataMenuItem extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 5),
child: Consumer<FishType>(
builder: (_, fishType, __) => ListTile(
tileColor: Colors.black54,
leading: IconButton(
icon: Icon(fishType.isFavorite ? Icons.star : Icons.star_border),
onPressed: () {
fishType.toggleFavorite();
},
),
title: Text(
fishType.name,
textAlign: TextAlign.center,
),
trailing: fishType.isUsed
? Switch(
value: fishType.isActive,
onChanged: (_) {
fishType.toggleActive();
},
)
: IconButton(
onPressed: () {
Provider.of<FishTypes>(context, listen: false)
.deleteFishType(fishType.id!);
},
icon: Icon(Icons.delete),
),
),
),
);
}
}
Is it possible without copy / paste -ing the whole code and only change the Consumer types?
Note: there are some other parts on the screens such as title, and a text input field, but these are the parts that cause me most of the headache so I copied here only these parts.
Thanks for the help in advance!

Why isn't Navigator.pop() refreshing data?

Hi guys I'm trying to build an app with flutter, so I have two screens HomeScreen() and RoutineScreen(). The first one is a Scaffold and in the body has a child Widget (a ListView called RoutinesWidget()) with all the routines. And the second one is to create a routine. The thing is, that when I create the routine, I use a button to pop to the HomeScreen() but it doesn't refresh the ListView (I'm guessing that it's because when I use Navigator.pop() it refreshes the Scaffold but not the child Widget maybe?)
HomeScreen() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Widgets/routines_widget.dart';
import 'package:workout_time/Widgets/statistics_widget.dart';
import 'package:workout_time/Screens/settings_screen.dart';
import 'package:workout_time/Screens/routine_screen.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;
List<Widget> _views = [
RoutinesWidget(),
StatisticsWidget(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kThirdColor,
appBar: AppBar(
leading: Icon(Icons.adb),
title: Text("Workout Time"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => SettingsScreen()))),
],
),
body: _views[_selectedIndex],
floatingActionButton: (_selectedIndex == 1)
? null
: FloatingActionButton(
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RoutineScreen(null)));
setState(() {});
},
child: Icon(
Icons.add,
color: kSecondColor,
size: 30.0,
),
elevation: 15.0,
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
bottomItems(Icon(Icons.fitness_center_rounded), "Routines"),
bottomItems(Icon(Icons.leaderboard_rounded), "Statistics"),
],
currentIndex: _selectedIndex,
onTap: (int index) => setState(() => _selectedIndex = index),
),
);
}
}
BottomNavigationBarItem bottomItems(Icon icon, String label) {
return BottomNavigationBarItem(
icon: icon,
label: label,
);
}
RoutinesWidget() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/Services/db_crud_service.dart';
import 'package:workout_time/Screens/routine_screen.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Models/routine_model.dart';
class RoutinesWidget extends StatefulWidget {
#override
_RoutinesWidgetState createState() => _RoutinesWidgetState();
}
class _RoutinesWidgetState extends State<RoutinesWidget> {
DBCRUDService helper;
#override
void initState() {
super.initState();
helper = DBCRUDService();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: helper.getRoutines(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Routine routine = Routine.fromMap(snapshot.data[index]);
return Card(
margin: EdgeInsets.all(1.0),
child: ListTile(
leading: CircleAvatar(
child: Text(
routine.name[0],
style: TextStyle(
color: kThirdOppositeColor,
fontWeight: FontWeight.bold),
),
backgroundColor: kAccentColor,
),
title: Text(routine.name),
subtitle: Text(routine.exercises.join(",")),
trailing: IconButton(
icon: Icon(Icons.delete_rounded),
color: Colors.redAccent,
onPressed: () {
setState(() {
helper.deleteRoutine(routine.id);
});
},
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RoutineScreen(routine))),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
color: kSecondColor,
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
}
RoutineScreen() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/Models/routine_model.dart';
import 'package:workout_time/Widgets/type_card_widget.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Services/db_crud_service.dart';
class RoutineScreen extends StatefulWidget {
final Routine _routine;
RoutineScreen(this._routine);
#override
_RoutineScreenState createState() => _RoutineScreenState();
}
class _RoutineScreenState extends State<RoutineScreen> {
DBCRUDService helper;
final _nameController = TextEditingController();
final _descriptionController = TextEditingController();
bool _type = true;
int _cycles = 1;
int _restBetweenExercises = 15;
int _restBetweenCycles = 60;
#override
void initState() {
super.initState();
helper = DBCRUDService();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
title: widget._routine != null
? Text(widget._routine.name)
: Text("Create your routine"),
actions: [
IconButton(
icon: Icon(Icons.done_rounded),
onPressed: createRoutine,
)
],
bottom: TabBar(
tabs: [
Tab(
text: "Configuration",
),
Tab(
text: "Exercises",
),
],
),
),
body: TabBarView(children: [
//_routine == null ? ConfigurationNewRoutine() : Text("WIDGET N° 1"),
ListView(
children: [
Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: [
Text(
"Name:",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(
width: 40.0,
),
Expanded(
child: TextField(
textAlign: TextAlign.center,
controller: _nameController,
),
),
],
),
),
SizedBox(
height: 20.0,
),
Card(
margin: EdgeInsets.all(15.0),
color: kSecondColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Container(
padding: EdgeInsets.all(15.0),
child: Column(
children: [
Text(
"Type",
style: TextStyle(fontSize: 25.0),
),
Row(
children: [
Expanded(
child: TypeCard(
Icons.double_arrow_rounded,
_type == true ? kFirstColor : kThirdColor,
() => setState(() => _type = true),
"Straight set",
),
),
Expanded(
child: TypeCard(
Icons.replay_rounded,
_type == false ? kFirstColor : kThirdColor,
() => setState(() => _type = false),
"Cycle",
),
),
],
),
],
),
),
),
SizedBox(
height: 20.0,
),
Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: [
Text(
"N° cycles:",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(
width: 40.0,
),
Expanded(
child: Text("Hello"),
),
],
),
),
SizedBox(
height: 20.0,
),
],
),
Text("WIDGET N° 2"),
]),
),
);
}
void createRoutine() {
List<String> _exercises = ["1", "2"];
List<String> _types = ["t", "r"];
List<String> _quantities = ["30", "20"];
Routine routine = Routine({
'name': _nameController.text,
'description': "_description",
'type': _type.toString(),
'cycles': 1,
'numberExercises': 2,
'restBetweenExercises': 15,
'restBetweenCycles': 60,
'exercises': _exercises,
'types': _types,
'quantities': _quantities,
});
setState(() {
helper.createRoutine(routine);
Navigator.pop(context);
});
}
}
Any idea what can I do to make it work? Thank you
Make it simple
use Navigator.pop() twice
so that the current class and old class in also removed
from the stack
and then use Navigator.push()
When you push a new Route, the old one still stays in the stack. The new route just overlaps the old one and forms like a layer above the old one. Then when you pop the new route, it will just remove the layer(new route) and the old route will be displayed as it was before.
Now you must be aware the Navigator.push() is an asynchronous method and returns a Future. How it works is basically when you perform a Navigator.push(), it will push the new route and will wait for it to be popped out. Then when the new route is popped, it returns a value to the old one and that when the future callback will be executed.
Hence the solution you are looking for is add a future callback like this after your Navigator.push() :
Navigator.push(context,
MaterialPageRoute(builder: (context) => SettingsScreen())
).then((value){setState(() {});}); /// A callback which is executed after the new route will be popped. In that callback, you simply call a setState and refresh the page.

How can I access the innermost documents in nested collection structures?

As seen in the picture, there is a collection structure within the firestore. I want to show it with a listview by reaching the document information at the end. But I can't view it on the screen.
Code here:
#override
Widget build(BuildContext context) {
randevular = databaseRef
.collection(
'kuaforumDB/$_salonID/BekleyenRandevular/')
.snapshots();
return StreamBuilder<QuerySnapshot>(
stream: randevular,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if(!snapshot.hasData) {
return Column(
children:<Widget> [
SizedBox(
height: 100,
),
Center(
child: Image.asset("assets/images/icons/fon.webp",matchTextDirection: true,
height: 140.0,
width: 140.0,
),),
SizedBox(
height: 20
),
Center(
child: new Text('Henüz bir randevu oluşturmadınız.')
)
],
);
}
else if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: new Center(
child: new CircularProgressIndicator(
value: null,
strokeWidth: 7.0,
),
)
);
} else {
return ListView(
children: snapshot.data.documents
.map((document) {
var query = databaseRef
.collection('kuaforumDB/')
.document('$_salonID')
.collection('BekleyenRandevular')
.document(document.documentID)
.collection('get')
.snapshots();
return StreamBuilder<QuerySnapshot> (
stream: query,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot2){
if (!snapshot2.hasData) return Text("Loading...");
return ListView(
children: snapshot2.data.documents
.map((DocumentSnapshot doc) => Card(
child: ListTile(
leading: IconButton(
tooltip: '',
icon: const Icon(Icons.check_circle, color: Colors.red,),
color: doc['randevuTarih']
.toDate()
.isBefore(DateTime.now())
? Colors.green
: Colors.orangeAccent,
iconSize: 30,
onPressed: () {},
),
title: Text(AppConstants.formatter
.format((doc['randevuTarih'].toDate())
.add(Duration(hours: 0)))
.toString()),
subtitle: Text('Randevu Onay Bekleniyor.'),
trailing: Icon(Icons.keyboard_arrow_right,
color: Colors.grey, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (content) => MyPendingDetailPage(
salonID: _salonID.toString(),
userID: mPhone,
randevuID:
doc.documentID.toString(),
randevuTarih: AppConstants
.formatter
.format((doc['randevuTarih']
.toDate())
.add(Duration(hours: 0)))
.toString(),
randevuHizmet: doc['hizmetler'],
randevuFiyat:
doc['fiyat'].toString(),
randevuSure:
doc['sure'].toString(),
randevuFavori:
doc['favori'] == null
? false
: doc['favori'],
randevuBittimi:
doc['randevuTarih']
.toDate()
.isBefore(
DateTime.now())
? true
: false,
ayBasi: startofmonth,
sonrandevu : doc['randevuTarih'],
)));
}, )))
.toList(),
);
},
);
}).toList());
}
});
}
Using nested Listview in the code above may have caused a question. But I don't know how to solve this. When I check it, I see that I can actually pull the data, but I can't show it on the screen.

RangeError : RangeError(index) : invalid value : only valid is 0: 3

I display the products available in my basket in this Screen Basket which works very well. I use a dismissible to be able to delete an item from my basket. And I often get the error from time to time: RangeError: RangeError (index): invalid value: only valid is 0: 3. What to do please?
Here is the interface of my basket. Basket_Screen. can flutter clean be the best solution? I'm afraid to try it. What exactly does flutter clean do? I need a solution please
class PanierScreen extends StatefulWidget {
#override
_PanierScreenState createState() => _PanierScreenState();
}
class _PanierScreenState extends State<PanierScreen> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
List<Produit> produits = Provider.of<Panier>(context).produits ;
Panier _panier = Provider.of<Panier>(context, listen : false);
super.build(context);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Panier",
home: Scaffold(
appBar: AppBar(
title: Text("Panier"),
centerTitle: true,
),
body: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(child: ListView.separated(
itemCount: produits.length,
itemBuilder: (BuildContext context, int index) {
return Dismissible(
key: UniqueKey(),
direction: DismissDirection.endToStart,
background: new Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: Icon(
Icons.delete, color: Colors.white,),
),
Spacer(),
Text("Supprimer", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),)
],
),
color: Colors.red,
),
onDismissed: (direction) {
// _panier.retirerProduit(produits[index]);
produits.removeAt(index);
print(produits.length);
Scaffold.of(context).showSnackBar(new SnackBar(
content: new Text("Produit supprimé du panier"),
duration: Duration(seconds: 3),)); },
child: Card (
child: ListTile(
leading: CircleAvatar(
child: FittedBox(
child: Text("${produits[index].prixvente} FCFA"),
),
),
title: Text("${produits[index].designation}".toUpperCase(), style: TextStyle(fontWeight: FontWeight.bold),),
subtitle: Text("Total : ${produits[index].quantite_vendue * produits[index].prixvente} FCFA", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.red[300]), ),
trailing: Text("${produits[index].quantite_vendue.toString()} x", style: TextStyle(fontWeight: FontWeight.bold),),
),
),
);
},
separatorBuilder: (BuildContext context, int index) => Divider(),
))
],
),
),
);
}
#override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}
flutter clean will rebuild the /build folder (Flutter's Build Cache). It's a safe command to run, but I don't think it has anything to do with your problem.
Seems like you're removing and item at some index from your List produits them trying to access it latter in the code.