I'm using the image_picker package to read images and take them using the camera.
I'm also using the provider package to manage the changes in results.
The app is about ads for selling stuff, when adding a new ad it is added successfully.
the problem is that the ad main image is not showing until I make a hot reload, and before reloading it shows an error.
Unable to load asset: /storage/emulated/0/Android/data/com.bkh.ads/files/Pictures/d2abeed9-3dfa-44b4-a032-ddefff58762e2465964411313585659.jpg
once I make a hot reload the ad image gets shown correctly and the error vanishes.
This is how I'm using image_picker:
Future _setAdMainImage() async {
String _method;
await showModalBottomSheet(
context: context,
builder: (context) => Container(
height: 105,
child: Column(
children: [
Container(
height: 50,
child: RaisedButton(
color: ColorPalette.PRIMARY_COLOR,
onPressed: () {
_method = 'Camera';
Navigator.of(context).pop();
},
child: Center(
child: Text(
'Image From Camera',
textDirection: TextDirection.rtl,
style: TextStyle(
fontSize: 18,
color: ColorPalette.WHITE_TEXT_ICONS_COLOR,
),
),
),
),
),
SizedBox(
height: 5,
),
Container(
height: 50,
child: RaisedButton(
color: ColorPalette.PRIMARY_COLOR,
onPressed: () {
_method = 'Gallery';
Navigator.of(context).pop();
},
child: Center(
child: Text(
'Image From Gallery',
textDirection: TextDirection.rtl,
style: TextStyle(
fontSize: 18,
color: ColorPalette.WHITE_TEXT_ICONS_COLOR,
),
),
),
),
),
],
),
),
);
if (_method != null) {
final _pickedFile = await _imagePicker.getImage(
source: _method == 'Camera' ? ImageSource.camera : ImageSource.gallery,
);
setState(() {
_image = File(_pickedFile.path);
});
_method = null;
}
}
This is how I'm adding the new ad object using the provider:
void addVehicleAd(VehicleAd vehicleAd) {
_vehicleAds.add(vehicleAd);
notifyListeners();
}
This is how I'm showing the results:
#override
Widget build(BuildContext context) {
_data = ModalRoute.of(context).settings.arguments as Map<String, dynamic>;
_ads = Provider.of<VehicleAds>(context).carAds;
return Scaffold(
body: ListView.builder(
itemCount: _ads.length,
itemBuilder: (context, index) => AdCard(
id: _ads[index].id,
image: _ads[index].image,
price: _ads[index].price,
label: _ads[index].label,
date: _ads[index].date,
),
),
);
}
And this is the AdCard widget:
class AdCard extends StatelessWidget {
final int id;
final String label, image;
final int price;
final DateTime date;
AdCard({
#required this.id,
#required this.label,
#required this.price,
#required this.image,
#required this.date,
});
#override
Widget build(BuildContext context) {
var _height = MediaQuery.of(context).size.height;
return InkWell(
child: Card(
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
width: 2,
color: ColorPalette.ACCENT_COLOR,
),
),
child: Stack(
children: <Widget>[
Container(
height: 250,
width: double.infinity,
child: Hero(
tag: id,
child: Image(
image: AssetImage(image),
fit: BoxFit.cover,
),
),
),
Positioned(
right: 10,
bottom: 10,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.black.withOpacity(.5),
),
child: Text(
label,
textDirection: TextDirection.rtl,
textAlign: TextAlign.center,
style: TextStyle(
height: 1,
color: ColorPalette.WHITE_TEXT_ICONS_COLOR,
),
),
),
),
Positioned(
left: 10,
bottom: 10,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.black.withOpacity(.5),
),
child: Text(
'$price',
textDirection: TextDirection.rtl,
textAlign: TextAlign.center,
style: TextStyle(
color: ColorPalette.WHITE_TEXT_ICONS_COLOR,
),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 10),
child: Align(
alignment: Alignment.topCenter,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.black.withOpacity(.5),
),
child: Text(
'${date.day}/${date.month}/${date.year}',
textDirection: TextDirection.rtl,
textAlign: TextAlign.center,
style: TextStyle(
color: ColorPalette.WHITE_TEXT_ICONS_COLOR,
),
),
),
),
),
],
),
),
);
}
}
I have no idea where the wrong code is...
Any help would be appreciated
AssetImage widget gets from your asset resource.
For images taken by imagepicker, use Image.file.
Image.file(/* your file */, fit: BoxFit.cover,)
Related
I am trying to create a list of items in flutter with listView.builder, i am also using using elevatedButton in my itemBuilder.
i applied setState in button
i want to change value of single item only
but that is applying to every value in list
here is my code
import 'dart:developer';
import 'package:counter_button/counter_button.dart';
import 'package:flutter/material.dart';
import 'package:fooddeliveryapp/apis.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'models/restrauntmenu.dart';
class RestrauntPage extends StatefulWidget {
final String restrauntId;
final String restrauntName;
final String restrauntType;
final String restrauntApproxBill;
final String restrauntTagline;
const RestrauntPage(
{Key? key,
required this.restrauntId,
required this.restrauntType,
required this.restrauntApproxBill,
required this.restrauntTagline,
required this.restrauntName})
: super(key: key);
#override
State<RestrauntPage> createState() => _RestrauntPageState();
}
class _RestrauntPageState extends State<RestrauntPage> {
int _counterValue = 1;
bool itemAdded = false;
late Future<List<RestrauntMenu>> futureRestrauntMenu;
Future<List<RestrauntMenu>> fetchRestrauntMenu() async {
String restrauntId = widget.restrauntId;
final response = await http.get(Uri.parse(menuApi(restrauntId)));
if (response.statusCode == 200) {
final parsed = json.decode(response.body).cast<Map<String, dynamic>>();
return parsed
.map<RestrauntMenu>((json) => RestrauntMenu.fromMap(json))
.toList();
} else {
throw Exception('Failed to load album');
}
}
addToCartButton(
{required String? itemname,
required String? itemprice,
required int? itemCount,
required String? usermob,
required String? restrauntName}) {
return ElevatedButton(
clipBehavior: Clip.antiAliasWithSaveLayer,
style: ButtonStyle(
elevation: MaterialStateProperty.all(
6,
),
backgroundColor: MaterialStateProperty.all(Colors.white),
),
onPressed: () {
addToCart(
itemname: itemname,
itemprice: itemprice,
restrauntName: restrauntName,
usermob: usermob,
itemCount: itemCount);
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'ADD TO CART',
style: TextStyle(
color: Colors.green,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
);
}
counterButton() {
return Container(
decoration: const BoxDecoration(color: Colors.white),
child: CounterButton(
loading: false,
onChange: (int val) {
setState(() {
_counterValue = val;
});
},
count: _counterValue,
countColor: Colors.green,
buttonColor: Colors.black,
progressColor: Colors.black,
),
);
}
Future addToCart(
{required String? itemname,
required String? itemprice,
required int? itemCount,
required String? usermob,
required String? restrauntName}) async {
itemCount = _counterValue;
var url = addToCartApi(
itemname: itemname,
itemprice: itemprice,
itemCount: itemCount,
usermob: usermob,
restrauntName: restrauntName);
var response = await http.get(
Uri.parse(url),
);
if (response.statusCode == 200) {
setState(() {
itemAdded = true;
});
} else {
return false;
}
}
#override
void initState() {
futureRestrauntMenu = fetchRestrauntMenu();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
foregroundColor: Colors.black,
elevation: 0,
backgroundColor: Colors.white,
title: const Text('Restraunt Name'),
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.03,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.restrauntName,
style: const TextStyle(
color: Colors.black,
fontSize: 24,
fontWeight: FontWeight.w900,
),
),
// const SizedBox(height: 4),
Text(
widget.restrauntType,
style: const TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.normal,
),
),
Text(
widget.restrauntTagline,
style: const TextStyle(
color: Colors.black87,
fontSize: 12,
fontWeight: FontWeight.normal,
),
),
const SizedBox(
height: 6,
),
Container(
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RichText(
text: TextSpan(
children: [
const WidgetSpan(
child: Icon(
Icons.currency_rupee,
size: 16,
color: Color.fromARGB(255, 45, 174, 49),
),
),
TextSpan(
text: widget.restrauntApproxBill,
style: const TextStyle(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
),
],
),
const Spacer(),
Container(
decoration: BoxDecoration(
color: const Color.fromARGB(255, 49, 171, 53),
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RichText(
text: const TextSpan(
children: [
TextSpan(
text: '3.6',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
WidgetSpan(
child: Icon(
Icons.star,
color: Colors.white,
size: 18,
),
),
],
),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.03,
),
],
),
const SizedBox(
height: 12,
),
Expanded(
child: FutureBuilder<List<RestrauntMenu>>(
future: futureRestrauntMenu,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ExpansionTile(
initiallyExpanded: true,
childrenPadding: const EdgeInsets.all(8),
title: Text(
snapshot.data![index].catname!,
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
children: [
Row(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
snapshot.data![index].itemname!,
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
height: 4,
),
Text(
snapshot.data![index].itemPrice!,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
const SizedBox(
height: 4,
),
Text(
snapshot.data![index].itemDescription!,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
),
),
],
),
const Spacer(),
Stack(
alignment: Alignment.bottomCenter,
children: [
Padding(
padding:
const EdgeInsets.only(bottom: 16),
child: ClipRRect(
borderRadius:
BorderRadius.circular(10),
child: FittedBox(
child: Container(
height: MediaQuery.of(context)
.size
.height *
0.22,
width: MediaQuery.of(context)
.size
.width *
0.4,
decoration: BoxDecoration(
color: Colors.red,
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
'http://www.jfamoslogistics.com/images/${snapshot.data![index].itemimage!}',
),
),
),
),
),
),
),
Align(
alignment: Alignment.topRight,
child: itemAdded
? counterButton()
: addToCartButton(
itemprice: snapshot
.data![index].itemPrice,
itemname: snapshot
.data![index].itemname,
itemCount: _counterValue,
restrauntName:
widget.restrauntName,
usermob: '9354954343',
))
],
)
],
),
],
);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
],
),
),
),
);
}
}
please check and help me
please let me know if i am missing something or making some mistake in code
i have tried google everywhere but nothing works
thanks in advance
As Giseppe Colucci described, you are using a single counter variable for a list . You can follow the simple approach of using list. On State
int _counterValue = 1; will be replaced with List<int> _counterValue = []
After getting data initialize the list with default value.
if (snapshot.hasData) {
_counterValue =List.generate(snapshot.data!.length, (index) => 1); // you might prefer default value as 0 instead of 1
As for the counter button method we need to update specific index, therefore we will pass index here
counterButton(int index) {
return Container(
decoration: const BoxDecoration(color: Colors.white),
child: CounterButton(
loading: false,
onChange: (int val) {
setState(() {
_counterValue[index] = val;
});
},
count: _counterValue[index],
Now whenever we use this counterButton method we need to pass index and here we get index from listview.
child: itemAdded
? counterButton(index)
: addToCartButton(
That is because your itemAdded bool is only one, for every item in the list, you should make a map, like this:
{'id':true}
where id is the restaurant menu item, and true is whenever the item is selected or not.
If this is too hard for you, just use a simple list.
I'm developing a job search app that scrapes data from Indeed using Python which is being sent back to my Flutter UI as JSON data. The JSON data is being received successfully, however, Im getting an error of Null check operator used on a null value. The error appears to be stemming from the _jobSearch widget.
The relevant error-causing widget was ListView lib/ui/home_page.dart:256
Exception caught by scheduler library
Null check operator used on a null value
Here is the code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter_job_portal/theme/colors.dart';
import 'package:flutter_job_portal/theme/images.dart';
import 'package:flutter_job_portal/ui/bottom_menu_bar.dart';
import 'package:flutter_job_portal/ui/job_detail_page.dart';
String job = ""; //user's response will be assigned to this variable
String final_response = "";
final _formkey = GlobalKey<FormState>(); //key created to interact with the form
//function to validate and save user form
Future<void> _savingData() async {
final validation = _formkey.currentState.validate();
if (!validation) {
return;
}
_formkey.currentState.save();
}
Future<List<Job>> _getJobs() async {
final url = 'http://127.0.0.1:5000/job';
final response1 = await http.post(Uri.parse(url), body: json.encode({'job': job}));
final response2 = await http.get(Uri.parse(url));
final decoded = json.decode(response2.body);
List<Job> jobs = [];
for (var i in decoded) {
Job job = Job(i['Title'], i['Company'], i['Location'], i['Salary']);
jobs.add(job);
}
return jobs;
}
class Job {
final String title;
final String company;
final String location;
final String salary;
Job(this.title, this.company, this.location, this.salary);
}
class HomePage extends StatelessWidget {
const HomePage({Key key}) : super(key: key);
Widget _appBar(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Row(
children: [
CircleAvatar(
backgroundImage: AssetImage(Images.user1),
),
Spacer(),
IconButton(
icon: Icon(Icons.notifications_none_rounded),
onPressed: () {},
)
],
),
);
}
Widget _header(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 12),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Hello, Alex!",
style: TextStyle(
fontSize: 15,
color: KColors.subtitle,
fontWeight: FontWeight.w500,
)),
SizedBox(
height: 6,
),
Text("Swipe to find your future",
style: TextStyle(
fontSize: 20,
color: KColors.title,
fontWeight: FontWeight.bold)),
SizedBox(
height: 10,
),
Row(
children: [
Expanded(
child: Container(
height: 45,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: KColors.lightGrey,
borderRadius: BorderRadius.circular(10)),
child: Form(
key: _formkey,
child: TextFormField(
decoration: InputDecoration(
hintText: 'Search job title or keywords',
),
onSaved: (value) {
job =
value; //getting data from the user form and assigning it to job
},
),
),
),
),
SizedBox(
width: 16,
),
Container(
decoration: BoxDecoration(
color: KColors.primary,
borderRadius: BorderRadius.circular(10),
),
height: 40,
child: IconButton(
color: KColors.primary,
icon: Icon(Icons.search, color: Colors.white),
onPressed: () async {
_savingData();
_getJobs();
},
),
)
],
)
],
),
);
}
Widget _recommendedSection(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
margin: EdgeInsets.symmetric(vertical: 12),
height: 200,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Recommended",
style: TextStyle(fontWeight: FontWeight.bold, color: KColors.title),
),
SizedBox(height: 10),
Expanded(
child: ListView(
scrollDirection: Axis.horizontal,
children: [
_recommendedJob(context,
company: "Google",
img: Images.google,
title: "UX Designer",
sub: "\$45,000 Remote",
isActive: true),
_recommendedJob(context,
company: "DropBox",
img: Images.dropbox,
title: "Research Assist",
sub: "\$45,000 Remote",
isActive: false)
],
),
),
],
),
);
}
Widget _recommendedJob(
BuildContext context, {
String img,
String company,
String title,
String sub,
bool isActive = false,
}) {
return Padding(
padding: const EdgeInsets.only(right: 10),
child: GestureDetector(
onTap: () {
Navigator.push(context, JobDetailPage.getJobDetail());
},
child: AspectRatio(
aspectRatio: 1.3,
child: Container(
decoration: BoxDecoration(
color: isActive ? KColors.primary : Colors.white,
borderRadius: BorderRadius.circular(7),
),
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 40,
width: 40,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: isActive ? Colors.white : KColors.lightGrey,
borderRadius: BorderRadius.circular(7),
),
child: Image.asset(img),
),
SizedBox(height: 16),
Text(
company,
style: TextStyle(
fontSize: 12,
color: isActive ? Colors.white38 : KColors.subtitle,
),
),
SizedBox(height: 6),
Text(
title,
style: TextStyle(
fontSize: 14,
color: isActive ? Colors.white : KColors.title,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 6),
Text(
sub,
style: TextStyle(
fontSize: 12,
color: isActive ? Colors.white38 : KColors.subtitle,
),
),
],
),
),
),
),
);
}
Widget _jobSearch(BuildContext context) {
return new Container(
child: FutureBuilder(
future: _getJobs(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text('Loading...'),
));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index].location),
);
},
);
}
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: KColors.background,
bottomNavigationBar: BottomMenuBar(),
body: SafeArea(
child: Container(
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_appBar(context),
_header(context),
_recommendedSection(context),
_jobSearch(context)
],
),
),
),
),
);
}
}
I want to implement Tab Bar in my application having length 2 named "Need Help" and "Help Requests". In "Need Help" tab, I want my first container (i.e. Upload data to Firestore Database) and in "Help Requests" tab, I want my second container (i.e. Retrieve data from Firestore Database). I am new to flutter and will be very much grateful to you if you can help me.
Source code:
import 'package:chat_app/group_chats/group_info.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../constants.dart';
import '../global_data.dart';
class FinancialRoom extends StatelessWidget {
final String groupChatId, groupName;
FinancialRoom({required this.groupName, required this.groupChatId, Key? key})
: super(key: key);
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;
final _formKey = GlobalKey<FormState>();
TextEditingController amountValue = TextEditingController();
void onSend() async {
Map<String, dynamic> data = {
"amount": amountValue.text,
"sendBy": _auth.currentUser!.displayName,
"type": "text",
"time": FieldValue.serverTimestamp(),
};
amountValue.clear();
await _firestore
.collection('groups')
.doc(groupChatId)
.collection('chats')
.add(data);
}
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
title: Text(groupName),
actions: [
IconButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => GroupInfo(
groupName: groupName,
groupId: groupChatId,
),
),
),
icon: Icon(Icons.more_vert)),
],
),
body: SafeArea(
child: ListView(padding: EdgeInsets.all(20.0), children: [
Container(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 10.0,
),
TextFormField(
controller: amountValue,
decoration: InputDecoration(
hintText: 'Enter the amount you want',
labelText: 'Enter the amount you want',
prefixIcon: Icon(Icons.account_balance_wallet_outlined),
enabledBorder: kEnabledBorder,
focusedBorder: kFocusedBorder,
errorBorder: kErrorBorder,
focusedErrorBorder: kErrorBorder,
),
onTap: () {
},
// The validator receives the text that the user has entered.
validator: (value) {
if (value!.isEmpty) {
return 'Please enter the amount you want';
}
return null;
},
),
SizedBox(
height: kInputSpacing,
),
SizedBox(
width: double.infinity,
child: FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
color: Colors.blue,
textColor: Colors.white,
padding: EdgeInsets.only(top: 16.0, bottom: 16.0),
onPressed: onSend,
child: Text(
'SEND',
style: kButtonTextStyle,
),
),
),
],
),
),
Container(
height: size.height / 1.27,
width: size.width,
child: StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('groups')
.doc(groupChatId)
.collection('chats')
.orderBy('time')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
Map<String, dynamic> data =
snapshot.data!.docs[index].data()
as Map<String, dynamic>;
return messageTile(size, data);
},
);
} else {
return Container();
}
},
),
),
]),
),
);
}
Widget messageTile(Size size, Map<String, dynamic> data) {
return Builder(builder: (_) {
if (data['type'] == "text") {
return Container(
width: size.width,
alignment: data['sendBy'] == _auth.currentUser!.displayName
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 14),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.blue,
),
child: Column(
children: [
Text(
data['sendBy'],
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
SizedBox(
height: size.height / 200,
),
Text(
data['amount'],
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
)),
);
} else if (data['type'] == "img") {
return Container(
width: size.width,
alignment: data['sendBy'] == _auth.currentUser!.displayName
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 14),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 8),
height: size.height / 2,
child: Image.network(
data['amount'],
),
),
);
} else if (data['type'] == "notify") {
return Container(
width: size.width,
alignment: Alignment.center,
child: Container(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.black38,
),
child: Text(
data['message'],
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
);
} else {
return SizedBox();
}
});
}
}
It's very straightforward to implement a simple TabBar in your app. All you need is a TabController and two widgets called TabBar and TabBarView. Here is a simple example:
DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(tabs: [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
]),
),
body: TabBarView(children: [
// Tab 1
Container(color: Colors.red),
// Tab 2
Container(color: Colors.blue),
]),
),
);
Now all you need to do is to replace children inside TabBarView with whatever you want to display.
I have my Cart model as below
class Cart {
String description;
double unitCost;
double amount;
int quantity;
String color;
String imageURl;
Cart({
this.description,
this.unitCost,
this.amount,
this.color,
this.quantity,
this.imageURl,
});
}
And my CartData notifier class which has a _cartList with pre-loaded data as shown below:
class CartData extends ChangeNotifier {
List<Cart> _cartItems = [
Cart(
imageURl:
'https://s2.r29static.com/bin/entry/ebd/0,675,2000,1050/x,80/1929471/image.jpg',
description: 'Nike Air Max',
quantity: 3,
unitCost: 6000,
),
Cart(
color: 'red',
description: 'Nike Air Max',
quantity: 3,
unitCost: 3000,
),
Cart(
imageURl:
'https://s2.r29static.com/bin/entry/ebd/0,675,2000,1050/x,80/1929471/image.jpg',
description: 'Nike Air Max',
quantity: 6,
unitCost: 6000,
),
Cart(
color: 'purple',
description: 'Nike Air Max',
quantity: 2,
unitCost: 1000,
),
Cart(
color: 'purple',
description: 'Nike Air Max',
quantity: 2,
unitCost: 2000,
),
];
UnmodifiableListView<Cart> get cartItems {
return UnmodifiableListView(_cartItems);
}
int get cartItemsCount {
return _cartItems.length;
}
void addItemToCart(
String color,
double unitCost,
String description,
String imageURL,
int quantity,
) {
_cartItems.insert(
0,
Cart(
color: color,
unitCost: unitCost,
description: description,
imageURl: imageURL,
quantity: quantity,
),
);
notifyListeners();
}
void getTotalSum(Cart cart) {}
double getAmount(int qty, double unitCost) {
return qty * unitCost;
}
void upDateCartItem(Cart cartItem, bool isIncrementing) {
if (isIncrementing == true) {
cartItem.quantity++;
print(
'Cart item purchase quantity is: ${cartItem.quantity}, after incrementing from updateCartItem');
} else {
if (cartItem.quantity >= 1) {
cartItem.quantity--;
print(
'Cart item purchase quantity is: ${cartItem.quantity} after decrementing from updateCartItem');
}
if (cartItem.quantity == 0) {
deleteItemFromCart(cartItem);
}
print(cartItem.quantity);
}
cartItem.amount = cartItem.quantity * cartItem.unitCost;
print('Cart Item amount is: ${cartItem.amount} from updateCartItem');
notifyListeners();
}
void deleteItemFromCart(Cart cartItem) {
_cartItems.remove(cartItem);
notifyListeners();
}
}
I want to be able to add items on the fly to my existing list of Cart Items using my addItemToCart() method, and display them immediately on my shopping cart page.
In my shopping screen, I have a few declarations as below:
String defaultDescription = 'add description';
String paymentDescription = 'add description';
String amount = '0';
int cartTotal = 0;
However, I'm unable to add item(s) to cart from my shopping screen when I call the addItemToCart method as below:
_addToCart() {
if (amount == '' || amount == '0') {
print('add an amount');
_showToast(this.context, 'amount');
} else if (paymentDescription == defaultDescription) {
print('please enter a valid description to proceed');
_showToast(this.context, 'payment description');
} else {
print(amount);
print(paymentDescription);
//do something like add to cart
CartData cartData = Provider.of<CartData>(context, listen: false);
cartData.addItemToCart(
null,
double.parse(output),
paymentDescription,
null,
1,
);
}
setState(() {
paymentDescription = defaultDescription;
amount = '0';
});
}
Here's my checkout screen that displays the cart Items:
class CheckoutScreen extends StatefulWidget {
static const String id = 'checkout_screen';
#override
_CheckoutScreenState createState() => _CheckoutScreenState();
}
class _CheckoutScreenState extends State<CheckoutScreen> {
#override
void initState() {
super.initState();
CustomerNotifier customerNotifier =
Provider.of<CustomerNotifier>(context, listen: false);
customerNotifier.getCustomers(customerNotifier);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ChangeNotifierProvider(
create: (context) => CartData(),
child: Consumer<CartData>(
builder: (context, cartData, child) {
return Column(
children: <Widget>[
Row(
children: <Widget>[
IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: Colors.black26,
),
onPressed: () => Navigator.pop(context),
),
Text(
'Shopping cart',
style: TextStyle(
fontSize: 21.0,
color: Colors.black26,
fontWeight: FontWeight.bold,
),
),
],
),
Container(
margin: EdgeInsets.only(top: 8.0, bottom: 8.0),
height: MediaQuery.of(context).size.height / 16,
width: MediaQuery.of(context).size.width - 150,
decoration: BoxDecoration(
border: Border.all(color: kThemeStyleButtonFillColour),
borderRadius: BorderRadius.circular(5.0),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Customer drop down',
textAlign: TextAlign.center,
style: kRegularTextStyle,
maxLines: 2,
textDirection: TextDirection.ltr,
softWrap: true,
),
SizedBox(width: 5.0),
Icon(
Icons.keyboard_arrow_down,
color: kThemeStyleButtonFillColour,
size: 25,
),
],
),
),
Expanded(
flex: 4,
child: Container(
height: MediaQuery.of(context).size.height - 225,
padding: const EdgeInsets.only(
left: 16, right: 16, bottom: 8.0),
child: ListView.builder(
itemCount: cartData.cartItems.length,
shrinkWrap: true,
itemBuilder: (context, index) {
final cartItem = cartData.cartItems[index];
return cartData.cartItems.isEmpty
? Container(
child: Align(
alignment: Alignment.center,
child: Text('Your cart is empty')))
: Container(
color: Colors.white,
margin: EdgeInsets.symmetric(vertical: 6.0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
width: 80.0,
height: 70.0,
child: Center(
child: cartItem.imageURl == null
? Container(
padding:
EdgeInsets.all(4.0),
decoration: BoxDecoration(
color: Color((math.Random()
.nextDouble() *
0xFFFFFF)
.toInt())
.withOpacity(1.0),
borderRadius:
BorderRadius.circular(
20.0),
),
)
: Container(
padding:
EdgeInsets.all(4.0),
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
cartItem.imageURl),
),
borderRadius:
BorderRadius.circular(
20.0),
),
),
),
),
),
SizedBox(width: 12.0),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Container(
width: 100.0,
child: Text(
cartItem.description,
style: TextStyle(
fontWeight: FontWeight.bold),
),
),
SizedBox(height: 8.0),
Row(
children: <Widget>[
GestureDetector(
child: Container(
width: 20.0,
height: 20.0,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius:
BorderRadiusDirectional
.circular(4.0),
),
child: Icon(
Icons.remove,
color: Colors.white,
size: 15.0,
),
),
onTap: () {
cartData.upDateCartItem(
cartItem, false);
},
),
Padding(
padding:
const EdgeInsets.symmetric(
horizontal: 15.0),
child: Text(
'${cartItem.quantity}',
style: TextStyle(
fontWeight:
FontWeight.bold,
fontSize: 15.0),
),
),
GestureDetector(
child: Container(
width: 20.0,
height: 20.0,
decoration: BoxDecoration(
color:
kThemeStyleButtonFillColour,
borderRadius:
BorderRadiusDirectional
.circular(4.0),
),
child: Icon(
Icons.add,
color: Colors.white,
size: 15.0,
),
),
onTap: () {
cartData.upDateCartItem(
cartItem, true);
},
),
],
),
],
),
// Spacer(),
Expanded(
child: Text(
'${cartData.getAmount(cartItem.quantity, cartItem.unitCost)}',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15.0),
),
),
Expanded(
child: GestureDetector(
child: Icon(
Icons.delete_forever,
size: 25.0,
color: kThemeStyleButtonFillColour,
),
onTap: () => cartData
.deleteItemFromCart(cartItem),
),
),
],
),
);
},
),
),
),
],
);
},
),
),
),
);
}
}
What could I be missing here?
Calling your addItemToCart() should rebuild the children inside Consumer<CartData>() because of notifyListeners(). This should help you give some clue on what's happening.
Add a debugPrint('List length: ${cartData.cartItemsCount}') inside the Consumer to check if List _cartItems changes after adding an item.
I am developing an app with Flutter and I struggle with a problem: when I call the setState() method, the list I want to update does not update at all. I mean, it does not redrawing the widgets.
First of all, I have my list of custom widget, which is initialized with one element:
List<AdvFctPompa> _advFctPompaList = [AdvFctPompa(name: 'Bloc initial', temp: '4748425', imgPath: img2)];
My custom widget looks like this:
import 'package:flutter/material.dart';
class AdvFctPompa extends StatefulWidget {
final String imgPath;
final String name;
final String temp;
BoxConstraints constraint;
AdvFctPompa({
Key key,
this.imgPath,
#required this.name,
#required this.temp,
this.constraint,
}) : super(key: key);
#override
_AdvFctPompaState createState() => _AdvFctPompaState();
}
class _AdvFctPompaState extends State<AdvFctPompa> {
#override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: <Widget>[
Container(
height: widget.constraint.maxHeight * .6,
width: widget.constraint.maxWidth * .7,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
image: DecorationImage(
image: NetworkImage(widget.imgPath),
fit: BoxFit.cover,
),
),
),
SizedBox(height: 5),
Expanded(
child: Container(
height: widget.constraint.maxHeight,
width: widget.constraint.maxWidth * .7,
padding: const EdgeInsets.only(left: 5, right: 5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
widget.name,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
),
),
Text(
widget.temp + "ÂșC",
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 15,
color: Colors.grey,
),
),
],
),
),
SizedBox(height: 5),
Container(
height: 30,
width: 100,
decoration: BoxDecoration(
image: DecorationImage(
image: ExactAssetImage('assets/images/img6.png'),
fit: BoxFit.cover,
),
),
)
],
),
),
)
],
),
);
}
}
Second, I have a button for adding a new element in list:
GestureDetector(
child: Visibility(
visible: _heightAnimation.value > 90,
child: Icon(
Icons.add_to_queue,
color: Colors.white,
size: _iconSizeAnimation.value,
),
),
onTap: _addAdvFct,
)
And here is my method:
void _addAdvFct() {
print(_advFctPompaList.length);
_advFctPompaList.add(
AdvFctPompa(
name: 'Bloc initial',
temp: '4748425',
imgPath: img2,
constraint: constraints,
),
);
setState(() {});
}
I also tried:
void _addAdvFct() {
print(_advFctPompaList.length);
setState(() {
_advFctPompaList.add(
AdvFctPompa(
name: 'Bloc initial',
temp: '4748425',
imgPath: img2,
constraint: constraints,
),
);
});
}
And the way I want to draw my list is through a LayoutBuider:
Expanded(
child: LayoutBuilder(builder: (_, constraint) {
_initConstrains(constraint);
return ListView(
padding: const EdgeInsets.only(right: 20),
scrollDirection: Axis.horizontal,
children: _advFctPompaList,
);
}),
),
The thing is that when I press the button it show me in console that the number of list elements is increasing, but nothing is changing on the screen.
Am I missing something?
Try
void _addAdvFct() {
print(_advFctPompaList.length);
_advFctPompaList.add(
AdvFctPompa(
name: 'Bloc initial',
temp: '4748425',
imgPath: img2,
constraint: constraints,
),
);
setState(() {
_advFctPompaList;
});
}