How to read nested data with firestore in flutter? - flutter

I am display a list of products, searched plenty of articles and tutorials but didn't succeeded to display the list of units fetched from the firestore and the specific price of the unit from firestore. kindly help me to achieve it.
I want to display the unit in a dropdown list and the assigned price according to the unit, but unable to do so, as i am new to flutter and trying to do code with the help of tutorials.
product.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class Product with ChangeNotifier {
final String id;
final String title;
final String description;
final String imageUrl;
final String productCategoryName;
Product({
required this.id,
required this.title,
required this.description,
required this.imageUrl,
required this.productCategoryName,
});
}
class ProductProvider with ChangeNotifier {
List<Product> _products = [];
Future<void> fetchProducts() async {
await FirebaseFirestore.instance
.collection('products')
.get()
.then((QuerySnapshot productSnapshot) {
_products = [];
for (var element in productSnapshot.docs) {
_products.insert(
0,
Product(
id: element.data()['productId'],
title: element.data()['productTitle'],
description: element.data()['productDescription'],
imageUrl: element.data()['productImage'],
productCategoryName: element.data()['productCategory'],
),
);
}
});
}
List<Product> products() => _products;
List<Product> getByCatName(String catName) {
List<Product> catList = _products
.where((element) =>
element.productCategoryName.toLowerCase() == catName.toLowerCase())
.toList();
return catList;
}
Product getById(String prodId) {
return _products.firstWhere((element) => element.id == prodId);
}
List<Product> getBySearch(String search) {
List<Product> prodList = _products
.where((element) =>
element.title.toLowerCase().contains(search.toLowerCase()))
.toList();
notifyListeners();
return prodList;
}
}
feed_product.dart
import 'dart:io';
import 'package:badges/badges.dart';
import 'feed_product_dialog.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models_providers/product.dart';
import '../screens/inner_screens/product_details_screen.dart';
class FeedsProduct extends StatefulWidget {
const FeedsProduct({Key? key}) : super(key: key);
#override
_FeedsProductState createState() => _FeedsProductState();
}
class _FeedsProductState extends State<FeedsProduct> {
String unitValue = '1kg';
late Product product;
#override
Widget build(BuildContext context) {
final productAttribute = Provider.of<Product>(context);
return InkWell(
onTap: () {
Navigator.of(context).pushNamed(
ProductDetailsScreen.routeName,
arguments: productAttribute.id,
);
},
child: Stack(
children: \[
Container(
height: 300,
decoration: BoxDecoration(
border: Border.all(width: 1, color: Colors.black),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: \[
Center(
child: Container(
constraints: BoxConstraints(
minHeight: Platform.isIOS ? 170 : 140,
maxHeight: MediaQuery.of(context).size.height * 0.21),
child: Center(
child: Image.network(productAttribute.imageUrl,
fit: BoxFit.cover),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: \[
Text(
productAttribute.title,
maxLines: 1,
style: const TextStyle(
fontSize: 16,
overflow: TextOverflow.ellipsis,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: \[
DropdownButton<String>(
value: unitValue,
elevation: 16,
style: const TextStyle(color: Colors.black),
onChanged: (String? newValue) {
setState(() {
unitValue = newValue!;
});
},
items: <String>\['500gm', '1kg'\]
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
IconButton(
onPressed: () async {
return showDialog(
context: context,
builder: (ctx) => FeedsProductDialog(
product: productAttribute,
),
);
},
icon: const Icon(Icons.more_horiz),
),
\],
),
\],
),
)
\],
),
),
Badge(
toAnimate: true,
animationType: BadgeAnimationType.slide,
shape: BadgeShape.square,
badgeColor: Colors.deepOrange,
borderRadius: BorderRadius.circular(8),
badgeContent:
const Text('New', style: TextStyle(color: Colors.white)),
),
\],
),
);
}
}

Related

I tried to get data from firestore but display this error "The instance member 'mapRecords' can't be accessed in an initializer. " on Flutter

I tried to get data from firestore but display this error "The instance member 'mapRecords' can't be accessed in an initializer. " on Flutter.
Screenshots of errors
https://drive.google.com/drive/folders/1toBdn9h4-LgBLl5ybG63brgXmk5G0h3F?usp=sharing
my code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:kathana/utils/config.dart';
import '../screens/wordsBefore18/database/words12model.dart';
class uitry5 extends StatefulWidget {
const uitry5({Key? key}) : super(key: key);
#override
State<uitry5> createState() => _uitry5State();
}
class _uitry5State extends State<uitry5> {
List<String> wordList = [];
Future _fetch = Future(() async {
var records = await FirebaseFirestore.instance.collection('12words').get();
return mapRecords(records);
});
List<Words12> mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var wordList = records.docs
.map(
(words12) {
print(words12);
return
Words12(
id: words12.id,
wordName: words12['wordName'],
categoryName: words12['categoryName']
);}
.toList();
return wordList;
}
#override
void initState() {
super.initState();
print(wordList);
}
List<String> selectedWord = [];
List<String>? deSelectedWord = [];
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
return Scaffold(
body: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(Config.app_background4), fit: BoxFit.fill),
),
child: SafeArea(
child: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 14, right: 0),
child: Column(
children: [
SizedBox(
width: width * 0.94,
height: height * 0.30,
child: Column(
children: <Widget>[
const SizedBox(height: 16),
Wrap(
children: wordList.map(
(word) {
bool isSelected = false;
if (selectedWord!.contains(word)) {
isSelected = true;
}
return GestureDetector(
onTap: () {
if (!selectedWord!.contains(word)) {
if (selectedWord!.length < 50) {
selectedWord!.add(word);
deSelectedWord!.removeWhere(
(element) => element == word);
setState(() {});
print(selectedWord);
}
} else {
selectedWord!.removeWhere(
(element) => element == word);
deSelectedWord!.add(word);
setState(() {
// selectedHobby.remove(hobby);
});
print(selectedWord);
print(deSelectedWord);
}
},
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 5, vertical: 4),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 5, horizontal: 12),
decoration: BoxDecoration(
color: isSelected
? HexColor('#0000FF')
: HexColor('#D9D9D9'),
borderRadius:
BorderRadius.circular(18),
border: Border.all(
color: isSelected
? HexColor('#0000FF')
: HexColor('#D9D9D9'),
width: 2)),
child: Text(
word,
style: TextStyle(
color: isSelected
? Colors.black
: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600),
),
),
),
);
},
).toList(),
),
],
),
),
],
),
),
],
))),
),
);
}
}
model
import 'dart:convert';
Words12 words12FromJson(String str) => Words12.fromJson(json.decode(str));
String words12ToJson(Words12 data) => json.encode(data.toJson());
class Words12 {
Words12({
required this.id,
required this.wordName,
required this.categoryName,
});
String id;
String wordName;
String categoryName;
factory Words12.fromJson(Map<String, dynamic> json) => Words12(
id: json["id"],
wordName: json["wordName"],
categoryName: json["categoryName"],
);
Map<String, dynamic> toJson() => {
"id": id,
"wordName": wordName,
"categoryName": categoryName,
};
}
How do I resolve this and get data from the database
You can use late before future.
late Future _fetch = ...
Also I will prefer shifting mapRecords before _fetch and FutureBuilder for future.

Flutter - Variable in function available when building Widget

If I understood correctly (but clearly I don't) when you need a var available when you build the widget you need to call the function in the initState(). The function is 'void getDevices()'
I need var _documentsIds to build DropdownMenuItem.
var _documentsIds is docs ID from a query I make to FirebaseFirestore
How do I make _documentsIds available to be used in my Widget?
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'package:safegaurd/constants.dart';
import 'package:flutter_time_picker_spinner/flutter_time_picker_spinner.dart';
import 'package:safegaurd/screens/log_in_page.dart';
import 'package:safegaurd/components/buttons_navigate.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'new_device_page.dart';
class DeviceSelectionPage extends StatefulWidget {
const DeviceSelectionPage({Key? key}) : super(key: key);
static const String id = 'device_selection_page';
#override
State<DeviceSelectionPage> createState() => _DeviceSelectionPageState();
}
class _DeviceSelectionPageState extends State<DeviceSelectionPage> {
final _auth = FirebaseAuth.instance;
User? loggedInUser;
final firestoreInstance = FirebaseFirestore.instance;
final String devices = 'devices';
List? _documentsIds;
bool showSpinner = false;
String? selectedValue;
final bool checkOne = false;
Color sDeviceAlways = Colors.white54;
Color sDeviceTime = Colors.white54;
FontWeight sDeviceAlwaysW = FontWeight.normal;
FontWeight sDeviceTimeW = FontWeight.normal;
#override
void initState() {
super.initState();
getCurrentUser();
getDevices();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser;
if (user != null) {
getDevices();
};
} catch (e) {
print(e);
}
}
void getDevices() async {
var firebaseUser = FirebaseAuth.instance.currentUser?.email;
print('firebaseUser: $firebaseUser');
var query = await firestoreInstance
.collection(devices)
.where('email', isEqualTo: '$firebaseUser')
.get();
var _documentsIds = query.docs.map((doc) => doc.id).toList();
print('_documentsIds: $_documentsIds');
}
#override
Widget build(BuildContext context) {
print('_documentsIds: $_documentsIds');
return Scaffold(
appBar: AppBar(
leading: null,
actions: [
IconButton(
onPressed: () {
_auth.signOut();
Navigator.pop(context);
},
icon: Icon(Icons.close))
],
title: const Text('Device Selection page'),
),
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 40,
child: Text(
'SAFEGAURD',
style: kAppTextStyle,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 380,
padding: const EdgeInsets.symmetric(horizontal: 24.0),
decoration: BoxDecoration(
border: Border.all(
color: Colors.lightBlueAccent, width: 1.0),
borderRadius: kBorderRadius),
child: DropdownButtonHideUnderline(
child: DropdownButton(
hint: const Text(
'Safegaurd Devices',
style: TextStyle(color: Colors.white54, fontSize: 25),
),
style:
const TextStyle(color: Colors.orange, fontSize: 25),
borderRadius: kBorderRadius,
iconSize: 40,
elevation: 16,
onChanged: (value) {
setState(() {
selectedValue = value.toString();
setState(() {
selectedValue;
print(selectedValue);
});
});
},
value: selectedValue,
items: _documentsIds?.map((itemsList) {
return DropdownMenuItem<String>(
value: itemsList,
child: Text(itemsList),
);
}).toList(),
),
),
)
],
),
Row(
children: [
Buttons_Navigate(
colour: Colors.teal,
title: 'Save Settings',
onPressed: () {},
width: 200,
),
],
),
Row(
children: [
Buttons_Navigate(
colour: Colors.teal,
title: 'Claim new Device',
onPressed: () {
Navigator.pushNamed(context, NewDevicePage.id);
},
width: 200,
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Buttons_Navigate(
colour: Colors.orange,
title: 'Back',
onPressed: () {
Navigator.pushNamed(context, LogInPage.id);
},
width: 40)
],
)
],
)),
));
}
}
wall, you can use futureBuilder and show A CircularProgressBar until the query is done check the documentation here: FutureBuilder
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'package:safegaurd/constants.dart';
//import 'package:flutter_time_picker_spinner/flutter_time_picker_spinner.dart';
import 'package:safegaurd/screens/log_in_page.dart';
import 'package:safegaurd/components/buttons_navigate.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'new_device_page.dart';
class DeviceSelectionPage extends StatefulWidget {
const DeviceSelectionPage({Key? key}) : super(key: key);
static const String id = 'device_selection_page';
#override
State<DeviceSelectionPage> createState() => _DeviceSelectionPageState();
}
class _DeviceSelectionPageState extends State<DeviceSelectionPage> {
final _auth = FirebaseAuth.instance;
User? loggedInUser;
final firestoreInstance = FirebaseFirestore.instance;
final String devices = 'devices';
List? _documentsIds;
bool showSpinner = false;
//bool _isSelected1 = false;
//bool _isSelected2 = false;
//DateTime _dateTime = DateTime.now();
String? selectedValue;
final bool checkOne = false;
Color sDeviceAlways = Colors.white54;
Color sDeviceTime = Colors.white54;
FontWeight sDeviceAlwaysW = FontWeight.normal;
FontWeight sDeviceTimeW = FontWeight.normal;
#override
void initState() {
super.initState();
getCurrentUser();
getDevices();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser;
if (user != null) {
getDevices();
};
} catch (e) {
print(e);
}
}
Future<List<dynamic>> getDevices() async {
var firebaseUser = FirebaseAuth.instance.currentUser?.email;
print('firebaseUser: $firebaseUser');
var query = await firestoreInstance
.collection(devices)
.where('email', isEqualTo: '$firebaseUser')
.get();
List<String> _documentsIds = query.docs.map((doc) => doc.id).toList();
print('_documentsIds: $_documentsIds');
return _documentsIds;
}
#override
Widget build(BuildContext context) {
print('_documentsIds: $_documentsIds');
return Scaffold(
appBar: AppBar(
leading: null,
actions: [
IconButton(
onPressed: () {
_auth.signOut();
Navigator.pop(context);
},
icon: Icon(Icons.close))
],
title: const Text('Device Selection page'),
),
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 40,
child: Text(
'SAFEGAURD',
style: kAppTextStyle,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 380,
padding: const EdgeInsets.symmetric(horizontal: 24.0),
decoration: BoxDecoration(
border: Border.all(
color: Colors.lightBlueAccent, width: 1.0),
borderRadius: kBorderRadius),
child: DropdownButtonHideUnderline(
child: FutureBuilder<List> (
future: getDevices(),
builder: (BuildContext context, AsyncSnapshot<List> snapshot){
if (!snapshot.hasData) {
return const Text('Waiting Devices',style: TextStyle(color: Colors.white54, fontSize: 25));
} else {
return DropdownButton(
hint: const Text(
'Safegaurd Devices',
style: TextStyle(color: Colors.white54, fontSize: 25),
),
style:
const TextStyle(color: Colors.orange, fontSize: 25),
borderRadius: kBorderRadius,
iconSize: 40,
elevation: 16,
onChanged: (value) {
setState(() {
selectedValue = value.toString();
setState(() {
selectedValue;
print(selectedValue);
});
});
},
value: selectedValue,
items: snapshot.data?.map((_documentsIds) =>
DropdownMenuItem<String>(
value: _documentsIds,
child: Text(_documentsIds),
)
).toList(),
);
}
}
)
),
)
],
),
Row(
children: [
Buttons_Navigate(
colour: Colors.teal,
title: 'Save Settings',
onPressed: () {},
width: 200,
),
],
),
Row(
children: [
Buttons_Navigate(
colour: Colors.teal,
title: 'Claim new Device',
onPressed: () {
Navigator.pushNamed(context, NewDevicePage.id);
},
width: 200,
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Buttons_Navigate(
colour: Colors.orange,
title: 'Back',
onPressed: () {
Navigator.pushNamed(context, LogInPage.id);
},
width: 40)
],
)
],
)),
));
}

Provider not rebuilding on flutter

suddently from nowhere i came up with provider not re rendering my home page when it's updated. I've inspected it and it IS UPDATED. It has newer data when i change it in firebase but the UI won't re-render showing the new data. That's my code:
Main function
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:my_event_app/pages/HomePage/home_page.dart';
import 'package:my_event_app/pages/Login/login_page.dart';
import 'package:my_event_app/providers/User/user.dart';
import 'package:provider/provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserModel()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
// is not restarted.
primarySwatch: Colors.blue,
),
home: const Wrapper(),
),
);
}
}
class Wrapper extends StatelessWidget {
const Wrapper({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.userChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User? user = snapshot.data;
if (user == null) {
return const LoginPage();
}
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.snapshots(),
builder: (context, userSnapshot) {
if (userSnapshot.hasData) {
Provider.of<UserModel>(context, listen: true)
.fromJson(userSnapshot.data!.data());
return const HomePage();
}
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
});
} else {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
}
And this is the home page:
import 'package:flutter/material.dart';
import 'package:my_event_app/http/auth/user/sign_out.dart';
import 'package:my_event_app/pages/Create_Event/create_event_page.dart';
import 'package:my_event_app/pages/Onboarding/onboarding_page.dart';
import 'package:my_event_app/providers/User/user.dart';
import 'package:my_event_app/widgets/event_card_widget.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Consumer<UserModel>(builder: (context, user, child) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.help_outline, color: Colors.black, size: 30),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const OnboardingPage();
}));
},
),
actions: [
IconButton(
icon: const Icon(
Icons.arrow_forward_ios_sharp,
color: Colors.black,
),
onPressed: () {
signOut();
},
),
],
elevation: 0,
backgroundColor: Colors.white,
),
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
"https://cdnnmundo1.img.sputniknews.com/img/07e5/09/13/1116212032_100:0:1273:1173_1920x0_80_0_0_efb734331af13dfe11ff6d43293c60e2.png"),
),
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
color: Colors.orange[400],
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Center(
child: IconButton(
color: Colors.white,
onPressed: () {
// Navigate to add event widget
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return const CreateEventPage();
}));
},
icon: const Icon(Icons.add),
),
),
),
],
),
const SizedBox(height: 32),
SizedBox(
width: double.infinity,
child: Text('Welcome, ${user.name}',
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
fontFamily: "Roboto")),
),
const SizedBox(height: 32),
Container(
padding: const EdgeInsets.all(16),
height: 100,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(alignment: Alignment.center, children: [
SizedBox(
height: 45,
width: 45,
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation(Colors.orange[400]),
value: 14 / 20,
semanticsValue: "14/20",
color: Colors.black,
),
),
const Text("78%",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
fontFamily: "Roboto")),
]),
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text("Weekly progress",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
)),
Text("14/20 tasks completed"),
],
),
const Icon(Icons.bar_chart),
],
),
),
Container(
margin: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
Text("You have 5 tasks for today",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
)),
Icon(Icons.calendar_today_outlined)
],
),
),
_renderEvents(user),
],
),
),
),
);
});
}
}
Column _renderEvents(UserModel user) {
return Column(
children: [
for (var event in user.events)
EventCard(
eventId: event,
),
],
);
}
And here's the provider:
import 'package:flutter/material.dart';
class UserModel extends ChangeNotifier {
String _name = '';
String _surnames = '';
String _uid = '';
String _email = '';
List<dynamic> _events = [];
String get name => _name;
String get surnames => _surnames;
String get uid => _uid;
String get email => _email;
List<dynamic> get events => _events;
UserModel();
set name(String value) {
_name = value;
notifyListeners();
}
set surnames(String value) {
_surnames = value;
notifyListeners();
}
set uid(String value) {
_uid = value;
notifyListeners();
}
set email(String value) {
_email = value;
notifyListeners();
}
set events(List<dynamic> value) {
_events = value;
notifyListeners();
}
void addEvent(String event) {
_events.add(event);
notifyListeners();
}
void removeEvent(String event) {
_events.remove(event);
notifyListeners();
}
void updateUser(String name, String uid) {
name = name;
uid = uid;
notifyListeners();
}
void clearUser() {
_name = '';
_uid = '';
notifyListeners();
}
Map<String, dynamic> toJson() {
return {
'name': _name,
'surnames': _surnames,
'uid': _uid,
'email': _email,
'events': _events
};
}
fromJson(Object? json) {
try {
Map<dynamic, dynamic> map = json as Map<dynamic, dynamic>;
_name = map['name'];
_surnames = map['surnames'];
_uid = map['uid'];
_email = map['email'];
_events = map['events'];
} catch (e) {
print(e);
}
}
}
```
As you can see i use Consumer in order to read data and i have a change notifier in the begginig, but it won't re render and show for example new name if i change it in fireabase.
Thank you so much!
You are using fromJson method to update values in UserModel, but it does not call notifyListeners. Add notifyListeners(); to the end of this method:
fromJson(Object? json) {
try {
Map<dynamic, dynamic> map = json as Map<dynamic, dynamic>;
_name = map['name'];
_surnames = map['surnames'];
_uid = map['uid'];
_email = map['email'];
_events = map['events'];
notifyListeners(); // add this
} catch (e) {
print(e);
}
}
Also some other things:
Consider declaring class UserModel with ChangeNotifier instead of class UserModel extends ChangeNotifier.
fromJson methods usually are acting as factory methods, meaning these return a new instance, and don't set members in an existing instance.
Instead of Provider.of<UserModel>(context, listen: true).fromJson(userSnapshot.data!.data()); you can try: context.read<UserModel>().fromJson(userSnapshot.data!.data());. Here you don't really need listening, you just want to find the provider and call fromJson. Your Consumer is the one which is listening to the changes accordingly.

The Argument type 'Context' can't be assigned to the parameter type 'BuildContext' - Flutter

I'm getting an error on context, I also found a similar question here on stackoverflow but it didn't solve my problem. The answer to that question suggested to add import 'package:path/path.dart'; but still I'm having the same error. Here is my code below:
import 'dart:js';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:chewie/chewie.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:fluttershare/models/user.dart';
import 'package:fluttershare/pages/comments.dart';
import 'package:fluttershare/pages/home.dart';
import 'package:fluttershare/widgets/progress.dart';
import 'package:video_player/video_player.dart';
import 'package:path/path.dart';
class Post extends StatefulWidget {
final String postId;
final String ownerId;
final String username;
final String foodname;
final String placename;
final String cityname;
final String statename;
final String mediaUrl;
final double rating;
final dynamic likes;
Post({
this.postId,
this.ownerId,
this.username,
this.foodname,
this.placename,
this.cityname,
this.statename,
this.mediaUrl,
this.rating,
this.likes,
});
factory Post.fromDocument(DocumentSnapshot doc) {
return Post(
postId: doc['postId'],
ownerId: doc['ownerId'],
username: doc['username'],
foodname: doc['foodname'],
placename: doc['placename'],
cityname: doc['cityname'],
statename: doc['statename'],
mediaUrl: doc['mediaUrl'],
rating: doc['rating'],
likes: doc['likes'],
);
}
int getLikeCount(likes) {
if (likes == null) {
return 0;
}
int count = 0;
likes.values.forEach((val) {
if (val == true) {
count += 1;
}
});
return count;
}
#override
_PostState createState() => _PostState(
postId: this.postId,
ownerId: this.ownerId,
username: this.username,
foodname: this.foodname,
placename: this.placename,
cityname: this.cityname,
statename: this.statename,
mediaUrl: this.mediaUrl,
likes: this.likes,
likeCount: getLikeCount(this.likes),
);
}
class _PostState extends State<Post> {
final String currentUserId = currentUser?.id;
final String postId;
final String ownerId;
final String username;
final String foodname;
final String placename;
final String cityname;
final String statename;
final String mediaUrl;
String rating;
int likeCount;
Map likes;
bool isLiked;
_PostState({
this.postId,
this.ownerId,
this.username,
this.foodname,
this.placename,
this.cityname,
this.statename,
this.mediaUrl,
this.rating,
this.likes,
this.likeCount,
});
buildPostHeader() {
return FutureBuilder(
future: usersRef.document(ownerId).get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
User user = User.fromDocument(snapshot.data);
return ListTile(
leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(user.photoUrl),
backgroundColor: Colors.grey,
),
title: GestureDetector(
onTap: () => print('showing profile'),
child: Text(
user.username,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
trailing: IconButton(
onPressed: () => print('deleting post'),
icon: Icon(Icons.more_vert),
),
);
},
);
}
VideoPlayerController _controller;
Future<void> _initializeVideoPlayerFuture;
#override
void initState() {
_controller = VideoPlayerController.network(mediaUrl);
_initializeVideoPlayerFuture = _controller.initialize();
_controller.setLooping(true);
_controller.setVolume(1.0);
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
// Post Chewie Display...
buildPostImage() {
return FittedBox(
fit: BoxFit.contain,
child: mounted
? Chewie(
controller: ChewieController(
videoPlayerController: _controller,
aspectRatio: 16 / 9,
autoPlay: false,
autoInitialize: true,
looping: true,
),
)
: Container(),
);
}
handleLikePost() {
bool _isLiked = likes[currentUserId] == true;
if (_isLiked) {
postsRef
.document(ownerId)
.collection('userPosts')
.document(postId)
.updateData({'likes.$currentUserId': false});
setState(() {
likeCount -= 1;
isLiked = false;
likes[currentUserId] = false;
});
} else if (!_isLiked) {
postsRef
.document(ownerId)
.collection('userPosts')
.document(postId)
.updateData({'likes.$currentUserId': true});
setState(() {
likeCount += 1;
isLiked = true;
likes[currentUserId] = true;
});
}
}
buildPostFooter() {
return Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 40.0, left: 20.0)),
GestureDetector(
onTap: handleLikePost,
child: Icon(
isLiked ? Icons.favorite : Icons.favorite_border,
size: 28.0,
color: Colors.red,
),
),
Padding(padding: EdgeInsets.only(right: 20.0)),
GestureDetector(
onTap: () => showComments(
context,
postId: postId,
ownerId: ownerId,
mediaUrl: mediaUrl,
),
child: Icon(
Icons.supervised_user_circle,
size: 28.0,
color: Colors.blueAccent,
),
),
Padding(padding: EdgeInsets.only(right: 50.0)),
Icon(Icons.location_on, color: Colors.blueAccent),
Container(
margin: EdgeInsets.only(left: 2.0, top: 5.0, right: 10.0),
child: Text("$cityname " + "$statename",
style: TextStyle(color: Colors.blueAccent)),
),
],
),
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20.0),
child: Text(
"$likeCount likes",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20.0, top: 10.0, bottom: 5.0),
child: Text(
"$foodname ",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(padding: EdgeInsets.only(left: 20.0)),
Icon(
Icons.restaurant,
color: Colors.blueAccent,
),
Container(
color: Colors.blueAccent,
padding: EdgeInsets.all(5.0),
margin: EdgeInsets.only(left: 2.0),
child: Text(placename.toUpperCase(),
style: TextStyle(color: Colors.white)),
),
],
),
],
);
}
#override
Widget build(BuildContext context) {
isLiked = (likes[currentUserId] == true);
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
buildPostHeader(),
buildPostImage(),
buildPostFooter(),
Divider(),
Padding(padding: EdgeInsets.only(bottom: 10.0)),
],
);
}
}
showComments(BuildContext context,
{String postId, String ownerId, String mediaUrl}) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return Comments(
postId: postId,
postOwnerId: ownerId,
postMediaUrl: mediaUrl,
);
}));
}
The argument type 'Context' error... Image here:
The argument type Context can't be assigned to the parameter type BuildContext.
So you need to pass the BuildContext instead of Context type. Try passing the build context to the buildPostFooter method from widget's build method.
buildPostFooter(BuildContext context){
...
GestureDetector(
onTap: () => showComments(
context,
postId: postId,
ownerId: ownerId,
mediaUrl: mediaUrl,
),
child: Icon(
Icons.supervised_user_circle,
size: 28.0,
color: Colors.blueAccent,
),
),
...
}
Then in your widget's build method. I hope this will solve your problem.
buildPostFooter(context),
Set import as path
import 'package:path/path.dart' as path;

how to display data in flutter firestore provider

I want to display data from firestore using provider in flutter. i got stuck please help. below are my codes
//product display page
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopping/pages/product_details.dart';
import 'package:shopping/provider/app_provider.dart';
class Product extends StatefulWidget {
#override
_ProductState createState() => _ProductState();
}
class _ProductState extends State<Product> {
#override
Widget build(BuildContext context) {
final product = Provider.of<AppProvider>(context);
return GridView.builder(
itemCount: productList.length,
gridDelegate:new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2),
itemBuilder: (BuildContext context, int index){
return Padding(
padding:const EdgeInsets.all(4.0),
child:SingleProd(
//where i want to get the product details
prodName: product.featuredProducts[index].name.toString(),
),
);
}
);
}
}
class SingleProd extends StatelessWidget {
final prodName;
final prodPicture;
final prodOldPrice;
final prodPrice;
SingleProd({this.prodName, this.prodPicture,this.prodOldPrice,this.prodPrice});
#override
Widget build(BuildContext context) {
return Card(
child: Hero(tag: new Text("hero 1"),
child:
Material( child: InkWell(
onTap: ()=>Navigator.of(context).push(new MaterialPageRoute(builder: (context)=>ProductDetails(
//here we are passing the value of the products to Product detail page
productDetailName:prodName,
)
)
),
child:GridTile(
footer: Container(
color: Colors.white,
child: new Row(
children: <Widget>[
new Expanded(
child: new Text(prodName, style: TextStyle(fontWeight: FontWeight.bold, fontSize:16.0),),
),
new Text(
"\$$prodPrice", style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),)
],
)
),
child: Image.asset(prodPicture,
fit: BoxFit.cover,),
),
),
),
),
);
}
}
//product class
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/foundation.dart';
class Product{
static const ID = "id";
static const CATEGORY = "category";
static const NAME = "name";
static const PRICE = "price";
static const BRAND = "brand";
static const COLORS = "colors";
static const QUANTITY = "quantity";
static const SIZES = "sizes";
static const SALE = "sale";
static const FEATURED = "featured";
static const PICTURE = "picture";
String _id;
String _name;
String _brand;
String _category;
String _picture;
double _price;
int _quantity;
List _colors;
List _sizes;
bool _onSale;
bool _featured;
// getters
String get name => _name;
String get id => _id;
String get category => _category;
String get brand => _brand;
String get picture => _picture;
double get price => _price;
int get quantity => _quantity;
List get colors => _colors;
List get sizes => _sizes;
bool get onSale => _onSale;
bool get featured => _featured;
// named constructure
Product.fromSnapshot(DocumentSnapshot snapshot){
Map data = snapshot.data;
_name = data[NAME];
_id = data[ID];
_category = data[CATEGORY];
_brand = data[BRAND];
_price = data[PRICE];
_quantity = data[QUANTITY];
_colors = data[COLORS];
_onSale = data[SALE];
_featured = data[FEATURED];
_picture = data[PICTURE];
}
}
//provider page for the product
import 'package:flutter/material.dart';
import 'package:shopping/db/product.dart';
import 'package:shopping/models/product.dart';
class AppProvider with ChangeNotifier {
List<Product>_fearturedProducts=[];
//method
void _getFeaturedProducts()async{
_fearturedProducts=await _productService.getFeaturedProducts();
notifyListeners();
}
}
//connection to Firestore to collect data
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:shopping/models/product.dart';
class ProductService{
Firestore _firestore=Firestore.instance;
String collection="Products";
Future<List<Product>>getFeaturedProducts(){
_firestore.collection(collection).where('featured', isEqualTo:true).getDocuments()
.then((snap){
List<Product>featuredProducts=[];
snap.documents.map((snapshot)=> featuredProducts.add(Product.fromSnapshot(snapshot)));
return featuredProducts;
});
}
}
guys, I have managed to solve the question. The answer is as follow //product page
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopping/pages/product_details.dart';
import 'package:shopping/provider/app_provider.dart';
import 'package:shopping/models/product.dart';
class Products extends StatefulWidget {
#override
ProductsState createState() => ProductsState();
}
class ProductsState extends State<Products> {
List<Product> products;
#override
Widget build(BuildContext context) {
final productProvider = Provider.of<CRUDModel>(context);
return StreamBuilder<QuerySnapshot>(
stream: productProvider.fetchProductsAsStream(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
products = snapshot.data.documents
.map((doc) => Product.fromMap(doc.data, doc.documentID))
.toList();
return GridView.builder(
itemCount: products.length,
gridDelegate:new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2),
itemBuilder: (BuildContext context, index){
return Padding(
padding:const EdgeInsets.all(4.0),
child:SingleProd(
product:products[index]
// prodPicture: productList[index]['picture'],
//prodOldPrice: productList[index]['oldPrice'],
//prodPrice: productList[index]['price'],
),
);
}
);
}
else {
return Text('fetching');
}
}
);
}
}
class SingleProd extends StatelessWidget {
//final prodName;
//final prodPicture;
//final prodOldPrice;
//final prodPrice;
final Product product;
SingleProd({ #required this.product});
//SingleProd({product.picture});
#override
Widget build(BuildContext context) {
return Card(
child: Hero(tag: product.id,
child:
Material( child: InkWell(
onTap: ()=>Navigator.of(context).push(new MaterialPageRoute(builder: (context)=>ProductDetails(
//here we are passing the value of the products to Product detail page
productDetailName:product.name,
productDetailNewPrice:product.price,
productDetailPicture:product.picture,
//productDetailOldPrice:prodOldPrice,
//productDetailNewPrice:prodPrice,
//productDetailPicture: prodPicture,
)
)
),
child:GridTile(
footer: Container(
color: Colors.white,
child: new Row(
children: <Widget>[
new Expanded(
child: new Text(product.name, style: TextStyle(fontWeight: FontWeight.bold, fontSize:16.0),),
),
new Text(
'${product.price} \$', style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),)
],
)
),
child: Image.asset('assets/${product.picture}.jpg',
fit: BoxFit.cover,),
),
),
),
),
);
}
}
//product class
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:ui';
class Product {
String id;
String name;
String brand;
String category;
String picture;
double price;
int quantity;
List colors;
List sizes;
bool sale;
bool featured;
Product(
{this.id, this.name, this.brand,
this.category, this.picture,this.price,
this.quantity,this.colors,this.sizes,this.sale,this.featured}
);
Product.fromMap(Map snapshot,String id) :
id = id ?? '',
name= snapshot['name'] ?? '',
brand = snapshot['brand'] ?? '',
category = snapshot['category'] ?? '',
picture= snapshot['picture'] ?? '',
price= snapshot['price'] ?? '',
quantity= snapshot['quantity'] ?? '',
colors= snapshot['colors'] ?? '',
sizes= snapshot['sizes'] ?? '',
sale= snapshot['sale'] ?? '',
featured= snapshot['featured'] ?? '';
toJson() {
return {
"name": name,
"brand": brand,
"category": category,
"picture": picture,
"price": price,
"quantity": quantity,
"colors": colors,
"sizes": sizes,
"sale": sale,
"featured": brand,
};
}
}
//provider class for the product
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:shopping/db/Api.dart';
import 'package:shopping/models/product.dart';
class CRUDModel extends ChangeNotifier {
//Api _api = locator<Api>();
String path="Products";
Api _api= Api();
List<Product> products;
Future<List<Product>> fetchProducts() async {
var result = await _api.getDataCollection();
products = result.documents
.map((doc) => Product.fromMap(doc.data, doc.documentID))
.toList();
notifyListeners();
return products;
}
Stream<QuerySnapshot> fetchProductsAsStream() {
notifyListeners();
return _api.streamDataCollection();
}
Future<Product> getProductById(String id) async {
var doc = await _api.getDocumentById(id);
notifyListeners();
return Product.fromMap(doc.data, doc.documentID) ;
}
}
//connection to firestore
import 'package:cloud_firestore/cloud_firestore.dart';
class Api{
final Firestore _db = Firestore.instance;
String ref="Products";
//CollectionReference ref;
/*Api({this.path } ) {
ref = _db.collection(path);
}*/
Future<QuerySnapshot> getDataCollection() {
//return ref.getDocuments() ;
return _db.collection(ref).where('featured', isEqualTo:true).getDocuments();
}
Stream<QuerySnapshot> streamDataCollection() {
// return ref.snapshots() ;
//return _db.snapshots(ref).getDocuments();
return _db.collection(ref).snapshots();
}
Future<DocumentSnapshot> getDocumentById(String id) {
// return ref.document(id).get();
return _db.document(id).get();
}
Future<void> removeDocument(String id){
//return ref.document(id).delete();
return _db.document(id).delete();
}
Future<DocumentReference> addDocument(Map data) {
// return ref.add(data);
return _db.collection(ref).add(data);
}
Future<void> updateDocument(Map data , String id) {
//return ref.document(id).updateData(data) ;
return _db.document(ref).updateData(data);
}
}
//homepage where i displayed the products
import 'package:flutter/material.dart';
import 'package:carousel_pro/carousel_pro.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:shopping/commons/common.dart';
import 'package:provider/provider.dart';
import 'package:shopping/provider/app_provider.dart';
import '../provider/user_provider.dart';
//My packages imports
import 'package:shopping/componets/horizontal_listview.dart';
import 'package:shopping/componets/product.dart';
import 'package:shopping/pages/cart.dart';
import 'package:shopping/pages/login.dart';
class HomePage extends StatefulWidget {
// List<Product> products;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController _searchController = new TextEditingController();
//final FirebaseAuth _firebaseAuth=FirebaseAuth.instance;
#override
Widget build(BuildContext context) {
final user = Provider.of<UserProvider>(context);
final productProvider=Provider.of<CRUDModel>(context);
Widget image_carousel = new Container(
height: 200.0,
child: new Carousel(
boxFit: BoxFit.cover,
images: [
AssetImage('images/c1.jpg'),
AssetImage('images/m1.jpeg'),
AssetImage('images/m2.jpg'),
AssetImage('images/w1.jpeg'),
AssetImage('images/w3.jpeg'),
AssetImage('images/w4.jpeg'),
],
autoplay:true,
animationCurve: Curves.fastOutSlowIn,
animationDuration: Duration(milliseconds:1000 ),
dotSize: 4.0,
indicatorBgPadding: 8.0,
dotBgColor: Colors.transparent,
),
);
return Scaffold(
appBar: new AppBar(
iconTheme: IconThemeData(color: blue),
elevation: 0.1,
backgroundColor: white,
title: Material(
borderRadius: BorderRadius.circular(20),
color: Colors.grey[50],
elevation: 0.0,
child: TextFormField(
controller: _searchController,
decoration: InputDecoration(
hintText: "Search",
border: InputBorder.none,
),
validator: (value) {
if (value.isEmpty) {
return "The Search field cannot be empty";
}
return null;
}),
),
actions: <Widget>[
new IconButton(
icon: Icon(
Icons.search,
color: blue,
),
onPressed: () {},
),
new IconButton(
icon: Icon(
Icons.shopping_cart,
color: blue,
),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => new Cart()));
}),
],
),
drawer: new Drawer(
child: new ListView(
children: <Widget>[
//drawer header
new UserAccountsDrawerHeader(
accountName: Text("Afolabi"),
accountEmail: Text("mtreal62#gmail.com"),
currentAccountPicture: GestureDetector(
child: new CircleAvatar(
backgroundColor: Colors.grey,
child: Icon(
Icons.person,
color: Colors.white,
),
),
),
decoration: BoxDecoration(
color: blue,
),
),
//body
InkWell(
onTap: () {},
child: ListTile(
title: Text("Home Page"),
leading: Icon(
Icons.home,
color: blue,
),
),
),
InkWell(
onTap: () {},
child: ListTile(
title: Text("My Account"),
leading: Icon(
Icons.person,
color: blue,
),
),
),
InkWell(
onTap: () {},
child: ListTile(
title: Text("My Orders"),
leading: Icon(
Icons.shopping_basket,
color: blue,
),
),
),
InkWell(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => new Cart()));
},
child: ListTile(
title: Text("Shopping Cart"),
leading: Icon(
Icons.shopping_cart,
color: blue,
),
),
),
InkWell(
onTap: () {},
child: ListTile(
title: Text("Favourites"),
leading: Icon(
Icons.favorite,
color: blue,
),
),
),
Divider(),
InkWell(
onTap: () {},
child: ListTile(
title: Text("Settings"),
leading: Icon(
Icons.settings,
),
),
),
InkWell(
onTap: () {},
child: ListTile(
title: Text("About"),
leading: Icon(
Icons.help,
),
),
),
InkWell(
onTap: () {
user.signOut();
// changeScreenReplacement(context, Login());
},
child: ListTile(
title: Text("Log Out"),
leading: Icon(
Icons.transit_enterexit,
),
),
),
],
),
),
body: new Column(
children: <Widget>[
//Image Carousel for the home Page Banner
image_carousel,
//padding widget after carousel
new Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
alignment: Alignment.centerLeft,
child: new Text("Categories"),
),
),
//Horizontal layout start from here
HorizontalList(),
//End of the horizontal layout
//padding widget for Recent products categories
new Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
alignment: Alignment.centerLeft,
child: new Text("Recent Products"),
),
),
// Text(appProvider.featuredProducts.length.toString(),
//style: TextStyle(color: Colors.black),),
Flexible(
child: Products(),
),
//Horizontal layout start from here
],
),
);
}
}
Future _signOut() async {
try {
await FirebaseAuth.instance.signOut();
} catch (e) {
print(e); // TODO: show dialog with error
}
}
You are never calling _getFeaturedProducts() from your AppProvider class. So _fearturedProducts will always be null
In class AppProvider you are calling method on undefined name:
_productService.getFeaturedProducts()
Every IDE should show you this error ex. In my Android Studio it looks like this: