How To Make Onboarding Screen Only One Time - flutter

I wanna make my onboarding screen shown only one time when install application in the device
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:introduction_screen/introduction_screen.dart';
import 'widget_tree.dart';
class IntroScreen extends StatefulWidget {
#override
_IntroScreenState createState() => _IntroScreenState();
}
class _IntroScreenState extends State {
final introKey = GlobalKey();
late final bool isFirstRun;
get child => null;
void onIntroEnd(context) {
Navigator.of(context).push(
MaterialPageRoute(builder: () => WidgetTree()),
);
}
Widget _buildFullscreenImage() {
return Image.asset(
'assets/images/intro1.png',
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
alignment: Alignment.center,
);
}
Widget _buildImage(String assetName, [double width = 350]) {
return Image.asset('assets/$assetName', width: width);
}
#override
Widget build(BuildContext context) {
const bodyStyle = TextStyle(fontSize: 19.0);
const pageDecoration = PageDecoration(
titleTextStyle: TextStyle(
color: Color.fromARGB(255, 29, 116, 182),
fontSize: 30.0,
fontWeight: FontWeight.w700,
fontFamily: 'assets/fonts/Ubuntu-B.ttf'),
bodyTextStyle: bodyStyle,
bodyPadding: EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 16.0),
pageColor: Color.fromARGB(0, 255, 255, 255),
imagePadding: EdgeInsets.zero,
);
return IntroductionScreen(
key: introKey,
globalBackgroundColor: Colors.white,
globalHeader: const Align(
alignment: Alignment.topRight,
child: SafeArea(
child: Padding(
padding: EdgeInsets.only(top: 16, right: 16),
// child: _buildImage('flutter.png', 100),
),
),
),
/*globalFooter: SizedBox(
width: double.infinity,
height: 60,
child: ElevatedButton(
style:const ButtonStyle(alignment:Alignment(1, 1)),
child: const Text(
'Skip!',
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
),
onPressed: () => _onIntroEnd(context),
),
),*/
pages: [
PageViewModel(
title: ('WELCOME'),
body:
'To the Mawed , which allows you to book a clinic easily . Choose the hospital that suits you and the doctor you want.',
image: Image.asset(
'assets/images/intro1.png',
width: 400,
height: 400,
fit: BoxFit.fitWidth,
alignment: AlignmentDirectional.bottomCenter,
),
decoration: pageDecoration,
),
PageViewModel(
title: "Book Now",
body: " From your home easly & Keep the ticket to show when asked",
image: Image.asset(
'assets/images/intro2.jpg',
width: 350,
height: 350,
fit: BoxFit.fitWidth,
alignment: AlignmentDirectional.center,
),
decoration: pageDecoration,
),
PageViewModel(
title: "Avoid",
body: "Crowding and wasting time, and go on time.",
image: Image.asset(
'assets/images/intro3.jpg',
width: 350,
height: 350,
fit: BoxFit.fitWidth,
alignment: AlignmentDirectional.center,
),
decoration: pageDecoration,
),
PageViewModel(
title: "Choose the hospital",
body: "That suits & the closest to you",
image: Image.asset(
'assets/images/intro4.jpg',
width: 350,
height: 350,
fit: BoxFit.fitWidth,
alignment: AlignmentDirectional.center,
),
decoration: pageDecoration,
),
PageViewModel(
title: "What do you feel ?",
body: "Choose the clinic or medical specialty carefully",
image: Image.asset('assets/images/intro5.png',
width: 350,
height: 350,
fit: BoxFit.fitWidth,
alignment: AlignmentDirectional.center),
decoration: pageDecoration,
),
PageViewModel(
title: "Get to know ",
body: "Your doctor's appointments and choose the time you want.",
image: Image.asset('assets/images/intro6.png',
width: 400,
height: 400,
fit: BoxFit.fitWidth,
alignment: AlignmentDirectional.bottomCenter),
decoration: pageDecoration,
),
],
onDone: () => _onIntroEnd(context),
onSkip: () => _onIntroEnd(context), // You can override onSkip callback
showSkipButton: false,
skipOrBackFlex: 0,
nextFlex: 0,
showBackButton: true,
//rtl: true, // Display as right-to-left
back: const Icon(Icons.arrow_back),
skip: const Text('Skip', style: TextStyle(fontWeight: FontWeight.w600)),
next: const Icon(Icons.arrow_forward),
done:
const Text('Book Now', style: TextStyle(fontWeight: FontWeight.w600)),
curve: Curves.fastLinearToSlowEaseIn,
controlsMargin: const EdgeInsets.all(16),
controlsPadding: kIsWeb
? const EdgeInsets.all(12.0)
: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 4.0),
dotsDecorator: const DotsDecorator(
size: Size(10.0, 10.0),
color: Color(0xFFBDBDBD),
activeSize: Size(22.0, 10.0),
activeShape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0)),
),
),
dotsContainerDecorator: const ShapeDecoration(
color: Colors.black87,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
),
);
}
}

The common way to handle this is to use the SharedPreferences library
1- When displaying the onboarding, store a bool like hasSeenOnboarding in the SharedPreferences with the value true
2- Right before displaying the onboarding, fetch the bool hasSeenOnboarding from the preferences.
If it's false, you can display the onboarding, otherwise you should skip it

You Can Use this Library
you will be able to save the data locally by configuring it only once

Related

How to make condition in Flutter

There is two files that have similar code, it is add and update area feature, that I decide to make it only one, with using condition.
You can see that two features have similar widgets, only different in texts.
Add Area Feature
Update Area Feature
In my case, when user want to adding area, the UI will display add area feature. When user want to updating area, the UI will display update area feature, but it only from one codebase.
Is it possible to make that kind of condition?
Here I copy paste the whole codes of add area feature. I don't have to copy paste update area because it has the similar codes.
class AddAreaItem extends StatefulWidget {
const AddAreaItem({super.key, this.isUpdate = false});
final bool isUpdate;
#override
State<AddAreaItem> createState() => _AddAreaItemState();
}
class _AddAreaItemState extends State<AddAreaItem> {
//--------- selectedCategory_1 variable
String selectedCategoryArea = '';
#override
Widget build(BuildContext context) {
return Container(
height: 366,
width: 514,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
height: 25,
),
SizedBox(
width: 434,
height: 42,
child: Wrap(
alignment: WrapAlignment.spaceBetween,
children: [
Text(
widget.isUpdate ? 'Add Area' : 'Update Area',
style: heading2(
color: ColorName.blackPrimary,
),
),
Padding(
padding: const EdgeInsets.only(top: 10),
child: InkWell(
child: SvgPicture.asset(
Assets.icons.closeIcon.path,
width: 16,
height: 16,
),
onTap: () => {
Navigator.pop(context),
},
),
)
],
),
),
//----------- TextField Code Area dan Category
SizedBox(
width: 435,
child: Wrap(
alignment: WrapAlignment.spaceAround,
children: [
Wrap(
direction: Axis.vertical,
children: [
Text(
'Area Code',
style: body1(
color: ColorName.blackPrimary,
),
),
SizedBox(
height: 10,
),
SizedBox(
width: 126,
height: 60,
child: TextField(
style: body1(color: ColorName.blackPrimary),
cursorColor: ColorName.blackPrimary,
decoration: InputDecoration(
hintText: 'Area Code',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(
color: ColorName.grey,
),
),
),
),
),
],
),
Wrap(
direction: Axis.vertical,
children: [
Text(
'Category Area',
style: body1(
color: ColorName.blackPrimary,
),
),
const SizedBox(
height: 10,
),
Container(
width: 288,
height: 56,
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
side: BorderSide(
style: BorderStyle.solid, color: ColorName.grey),
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
),
child: DropdownButton<String>(
icon: Padding(
padding: const EdgeInsets.only(right: 10, top: 8),
child: SvgPicture.asset(
Assets.icons.dropdownIcon.path,
fit: BoxFit.scaleDown,
),
),
style: body1(color: ColorName.blackPrimary),
items: <String>[
'Block',
'Fining Line',
].map((String value) {
return DropdownMenuItem(
value: value,
child: Text(value),
);
}).toList(),
hint: Padding(
padding: const EdgeInsets.only(top: 8, left: 10),
child: Text(
style: body1(color: ColorName.grey),
selectedCategoryArea.isEmpty
? 'Category Area'
: selectedCategoryArea),
),
borderRadius: BorderRadius.circular(10),
underline: const SizedBox(),
isExpanded: true,
onChanged: (value) {
if (value != null) {
setState(() {
selectedCategoryArea = value;
});
}
},
),
),
],
),
],
),
),
//----------- Tombol Add Area dan Cancel
SizedBox(
width: 425,
child: Wrap(
alignment: WrapAlignment.spaceBetween,
children: [
SizedBox(
width: 183,
height: 60,
child: OutlinedButton(
style: ButtonStyle(
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
side: MaterialStateProperty.all(
const BorderSide(
color: ColorName.darkBlue,
),
),
),
child: Text(
'Cancel',
style: subtitle1(
color: ColorName.darkBlue,
),
),
onPressed: () {
Navigator.pop(context);
},
),
),
//------------- Tombol add area
SizedBox(
width: 183,
height: 60,
child: OutlinedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
ColorName.darkBlue,
),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
side: MaterialStateProperty.all(
const BorderSide(
color: ColorName.darkBlue,
),
),
),
child: Text(
widget.isUpdate ? 'Add Area' : 'Update Area',
style: subtitle1(
color: ColorName.white,
),
),
onPressed: () {
if (widget.isUpdate) {
AddAreaSuccess().showCustomDialog(context);
} else {
UpdateAreaSuccess().showCustomDialog(context);
}
},
),
),
],
),
),
const SizedBox(
height: 25,
),
],
),
);
}
}
Firstly you need to define from where you know that the screen is updating screen or adding screen. I mean like
class AddAreaItem extends StatefulWidget {
final bool isUpdate;
const AddAreaItem({super.key, this.isUpdate=false});//IT MEANS THAT isUpdate is FALSE by default, but you can define it while creating AddAreaItem
...
}
Then in your widgets you can do like this:
Text(
widget.isUpdate?'Update Area':'Add Area',
//if it is inside Stateless widget then you use isUpdate? instead of widget.isUpdate
style: subtitle1(
color: ColorName.white,
),
),
Inside functions like onPressed you can just use like this:
onPressed: (){
if(widget.isUpdating){
AddAreaSuccess().showCustomDialog(context);
}else{...}
}
You can pass those two values in the properties when rendering this widget. Then check if the properties are null then you are trying to add new data. But if those contains the value then its updating the data.
Something like this
class AreaWidget extends StatefulWidget {
const AreaWidget({super.key, this.areaCode, this.areaName});
final int? areaCode;
final String? areaName;
#override
State<AreaWidget> createState() => _AreaWidgetState();
}
class _AreaWidgetState extends State<AreaWidget> {
#override
Widget build(BuildContext context) {
return Container(
child: Text(widget.areaCode == null ? "Add Area" : "Upate Area"),
);
}
}
You can then check value and make the changes according to the value. Even inside the function as well to call the specific add or update related code.

ReorderbleList with Draggable Widgets does not work

I have a ReorderdableList with 5 Widgets and each of them is Draggable.
The Draggable Widget works great, but it seems that this makes the onReorder function not work.
This is my ReorderableListView:
return Scaffold(
body: ReorderableListView(
onReorder: ((oldIndex, newIndex) {
print('onReorder');
}),
onReorderStart: (index) => print('reorder start'),
scrollDirection: Axis.horizontal,
children: [
for (final card in handCards)
HandCard(key: ValueKey(card), card, player, handOwner),
],
),
);
Each Handcard returns a Draggable Widget. Is there a way to make sure both still work?
Handcard Widget:
class HandCard extends ConsumerStatefulWidget {
HandCard(this.card, this.player, this.handOwner, {Key? key})
: super(key: key);
String handOwner;
dynamic card;
Player player;
#override
_HandCardState createState() => _HandCardState();
}
class _HandCardState extends ConsumerState<HandCard> {
#override
void initState() {
isVisibled = true;
// TODO: implement initState
super.initState();
print(isVisibled);
}
void setVisible() {
setState(() {
isVisibled = true;
});
}
late bool isVisibled;
#override
Widget build(BuildContext context) {
var handCardPickProvider =
ref.watch(handCardHighlightProvider(widget.card).notifier);
var handCardPick = ref.watch(handCardHighlightProvider(widget.card));
var gameStateProvider = ref.watch(GameStateProvider);
var handSize = ref.watch(handSizeProvider(widget.handOwner).state);
return Draggable<HandCard>(
onDragStarted: () {
// change state of current cards in hand
handSize.state = handSize.state - 1;
},
onDragEnd: (details) {
handSize.state = handSize.state + 1;
},
onDragCompleted: () {
setState(() {
isVisibled = true;
});
},
data: widget,
childWhenDragging: SizedBox.shrink(),
feedback: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(widget.card.imageLink),
fit: BoxFit.fill,
),
),
width: 100,
height: 150,
child: Stack(children: [
(widget.card is CharacterCard)
? Positioned(
right: 0,
left: 60,
child: Container(
color: Colors.white,
width: 40,
height: 13,
child: Center(
child: Text(
widget.card.power.toString(),
style: TextStyle(
color: Colors.black,
fontSize: 10,
fontWeight: FontWeight.bold),
),
),
),
)
: const SizedBox.shrink(),
Positioned(
left: 2,
child: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Center(
child: Text(
widget.card.cost.toString(),
style: TextStyle(color: Colors.white, fontSize: 12),
)),
))
]),
),
child: (isVisibled)
? InkWell(
onTap: () {
print('click');
// If it is the counter move and the card has a counter effect we can use it
if (gameStateProvider.gameSession!.moves.length != 0) {
Move currentMove = gameStateProvider.gameSession!
.moves[gameStateProvider.gameSession!.atMove - 1];
print(currentMove.moveType);
// Handle on counter effect
if (currentMove.moveType == 'on counter effect' &&
currentMove.toPlayer.id == widget.player.id) {
handCardPickProvider.highlightCard(widget.card);
}
}
},
child: Container(
margin: EdgeInsets.all(1),
decoration: BoxDecoration(
boxShadow: [
(handCardPick)
? BoxShadow(
color: Colors.white,
spreadRadius: 1,
blurRadius: 10)
: BoxShadow(
color: Colors.white,
spreadRadius: 0,
blurRadius: 0),
],
image: DecorationImage(
image: AssetImage(widget.card.imageLink),
fit: BoxFit.fill,
),
),
width: 100,
height: 150,
child: Stack(children: [
(widget.card is CharacterCard)
? Positioned(
right: 0,
left: 60,
child: Container(
color: Colors.white,
width: 40,
height: 13,
child: Center(
child: Text(
widget.card.power.toString(),
style: TextStyle(
color: Colors.black,
fontSize: 10,
fontWeight: FontWeight.bold),
),
),
),
)
: const SizedBox.shrink(),
Positioned(
left: 2,
child: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Center(
child: Text(
widget.card.cost.toString(),
style: TextStyle(color: Colors.white, fontSize: 12),
)),
))
]),
),
)
: SizedBox.shrink(),
);
}
}

:ui_dart_state.cc(209)] Unhandled Exception: NoSuchMethodError: The method '[]' was called on null. Receiver: null Tried calling: []("queryText")

hi everyone I have a flutter app and I want to integrate a chatbot with it I use dialog flow to create the bot and I writhe the code which is contain some commands for the screen and the messages but the code above are appearing to me + the response of the chatbot does not appear and I didn't know why , also I generate the json file and put it on the assets
this is my code
import 'package:bubble/bubble.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:dialogflow_flutter/dialogflowFlutter.dart';
import 'package:dialogflow_flutter/googleAuth.dart';
import 'package:dialogflow_flutter/language.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class chatBot extends StatefulWidget {
const chatBot({Key? key}) : super(key: key);
#override
_chatBotState createState() => _chatBotState();
}
class _chatBotState extends State<chatBot> {
final messageController = TextEditingController();
List<Map> messsages = [];
void response(query) async {
AuthGoogle authGoogle =
await AuthGoogle(fileJson: "Assets/images/service.json").build();
DialogFlow dialogflow =
DialogFlow(authGoogle: authGoogle, language: Language.english);
AIResponse aiResponse = await dialogflow.detectIntent(query);
setState(() {
messsages.insert(0, {
"data": 0,
"message": aiResponse.getListMessage()[0]["text"]["text"][0].toString()
});
});
print(aiResponse.getListMessage()[0]["text"]["text"][0].toString());
}
final messageInsert = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Chat bot",
),
),
body: Container(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 15, bottom: 10),
child: Text(
"Today, ${DateFormat("Hm").format(DateTime.now())}",
style: TextStyle(fontSize: 20),
),
),
Flexible(
child: ListView.builder(
reverse: true,
itemCount: messsages.length,
itemBuilder: (context, index) => chat(
messsages[index]["message"].toString(),
messsages[index]["data"]))),
SizedBox(
height: 20,
),
Divider(
height: 5.0,
color: Colors.greenAccent,
),
Container(
child: ListTile(
leading: IconButton(
icon: Icon(
Icons.camera_alt,
color: Colors.greenAccent,
size: 35,
),
onPressed: () {},
),
title: Container(
height: 35,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(15)),
color: Color.fromRGBO(220, 220, 220, 1),
),
padding: EdgeInsets.only(left: 15),
child: TextFormField(
controller: messageInsert,
decoration: InputDecoration(
hintText: "Enter a Message...",
hintStyle: TextStyle(color: Colors.black26),
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
),
style: TextStyle(fontSize: 16, color: Colors.black),
onChanged: (value) {},
),
),
trailing: IconButton(
icon: Icon(
Icons.send,
size: 30.0,
color: Colors.greenAccent,
),
onPressed: () {
if (messageInsert.text.isEmpty) {
print("empty message");
} else {
setState(() {
messsages.insert(
0, {"data": 1, "message": messageInsert.text});
});
response(messageInsert.text);
messageInsert.clear();
}
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
}),
),
),
SizedBox(
height: 15.0,
)
],
),
),
);
}
//for better one i have use the bubble package check out the pubspec.yaml
Widget chat(String message, int data) {
return Container(
padding: EdgeInsets.only(left: 20, right: 20),
child: Row(
mainAxisAlignment:
data == 1 ? MainAxisAlignment.end : MainAxisAlignment.start,
children: [
data == 0
? Container(
height: 60,
width: 60,
child: CircleAvatar(
backgroundImage: AssetImage("Assets/images/robot.jpg"),
),
)
: Container(),
Padding(
padding: EdgeInsets.all(10.0),
child: Bubble(
radius: Radius.circular(15.0),
color: data == 0
? Color.fromRGBO(23, 157, 139, 1)
: Colors.orangeAccent,
elevation: 0.0,
child: Padding(
padding: EdgeInsets.all(2.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
width: 10.0,
),
Flexible(
child: Container(
constraints: BoxConstraints(maxWidth: 200),
child: Text(
message,
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
))
],
),
)),
),
data == 1
? Container(
height: 60,
width: 60,
child: CircleAvatar(
backgroundImage: AssetImage("Assets/images/userphoto.jpeg"),
),
)
: Container(),
],
),
);
}
}
and this is the packages that I import on pubspec.yaml
dialogflow_flutter: ^0.0.3
bubble:
intl:
and this is the error appears to me
please help me

image_picker only showing images after hot reload. Flutter

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,)

trying to add to bag and to update prices and cups

I am trying to do this test TODOs: but i have been have issuses pls help: i am trying to Uncomment the _confirmOrderModalBottomSheet() method to show summary of order, Uncomment the setState() function to clear the price and cups, and Change the 'price' to 0 when this button is clicked Increment the _cupsCounter when 'Add to Bag' button is clicked, and to Call setState((){}) method to update both price and cups counter when 'Add to Bag' button is clicked
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Coffee Test',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.white,
),
home: MyHomePage(title: 'Coffee Test'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _selectedPosition = -1;
String _coffeePrice ="0";
int _cupsCounter =0;
int price = 0;
String _currency ="₦";
static const String coffeeCup ="images/coffee_cup_size.png";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: FlatButton(
onPressed: (){
//TODO: Uncomment the _confirmOrderModalBottomSheet() method to show summary of order
//_confirmOrderModalBottomSheet(totalPrice: "$_currency$price", numOfCups: "x $_cupsCounter");
},
child: Text("Buy Now",style: TextStyle(color: Colors.black87),),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18.0), side: BorderSide(color: Colors.blue))
),
actions: [
InkWell(
onTap: () {
//TODO: Uncomment the setState() function to clear the price and cups
//TODO: Change the 'price' to 0 when this button is clicked
setState(() {
this.price = -1;
this._cupsCounter = 0;
});
Icon(Icons.clear);
}),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
height: double.maxFinite,
alignment: Alignment.center,
child: Text("$_cupsCounter Cups = $_currency$price.00", style: TextStyle(fontSize: 18),),
),
)
],
),
body: Padding(padding: EdgeInsets.all(20), child: _mainBody(),) // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _mainBody(){
return SingleChildScrollView(
child: Container(
height: double.maxFinite,
width: double.maxFinite,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 0,
child: Stack(
children: [
Container(
width: double.maxFinite,
height: 250,
margin: EdgeInsets.only(left: 50, right: 50, bottom: 50, top: 60),
decoration: BoxDecoration(borderRadius:
BorderRadius.all(Radius.circular(180)),
color: Color.fromRGBO(239, 235, 233, 100)),
),
Container(
alignment: Alignment.center,
width: double.maxFinite,
height: 350,
child: Image.asset("images/cup_of_coffee.png", height: 300,),
)
],
)),
Padding(padding: EdgeInsets.all(10),),
Expanded(flex: 0,child: Text("Caffè Americano",
style: TextStyle(fontWeight: FontWeight.bold,
fontSize: 30),)),
Padding(padding: EdgeInsets.all(6),),
Expanded(flex: 0, child: Text("Select the cup size you want and we will deliver it to you in less than 48hours",
style: TextStyle(fontWeight: FontWeight.bold,
fontSize: 14, color: Colors.black45,),
textAlign: TextAlign.start,),
),
Container(
margin: EdgeInsets.only(top: 30, left: 20),
height: 55,
width: double.maxFinite,
alignment: Alignment.center,
child:Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
RichText(text: TextSpan(
text: _currency,
style: TextStyle(fontWeight: FontWeight.bold,
fontSize: 25, color: Colors.black87),
children: [
TextSpan(text: _coffeePrice, style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold))
]
),),
Padding(
padding: EdgeInsets.only(right: 15),
),
ListView.builder(itemBuilder: (context, index){
return InkWell(
child: _coffeeSizeButton(_selectedPosition == index,
index ==0? "S" : index ==1? "M": "L"),
onTap: (){
setState(() {
this._coffeePrice= index ==0? "300" : index ==1? "600": "900";
_selectedPosition = index;
});
},
);
}, scrollDirection: Axis.horizontal,
itemCount: 3, shrinkWrap: true,),
],),
),
Container(
margin: EdgeInsets.only(top: 30),
padding: EdgeInsets.all(10),
width: double.maxFinite,
height: 70,
child: FlatButton(onPressed: (){
//TODO: Currently _cupsCounter only show 1 when this button is clicked.
// TODO: Increment the _cupsCounter when 'Add to Bag' button is clicked'
//TODO: Call setState((){}) method to update both price and cups counter when 'Add to Bag' button is clicked
this._cupsCounter = 1;
this.price += int.parse(_coffeePrice);
}, child: Center(child: Text("Add to Bag",
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),)
,),
color: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50)
),
),
)
],
),
),
);
}
Widget _coffeeSizeButton(bool isSelected, String coffeeSize){
return Stack(
children: [
Container(alignment: Alignment.center, width: 55,
child: Text(coffeeSize, style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold,
color: isSelected? Colors.blue: Colors.black45),),),
new Container(
margin: EdgeInsets.only(right: 10),
child: Image.asset(coffeeCup, width:50, color: isSelected ? Colors.blue: Colors.black45,),
decoration: BoxDecoration(border: Border(top: BorderSide(color: isSelected? Colors.blue: Colors.black45,
width: isSelected? 2: 1), left: BorderSide(color: isSelected? Colors.blue: Colors.black45,
width: isSelected? 2: 1), bottom: BorderSide(color: isSelected? Colors.blue: Colors.black45,
width: isSelected? 2: 1), right: BorderSide(color: isSelected ?Colors.blue: Colors.black45 ,
width: isSelected? 2: 1)), borderRadius: BorderRadius.all(Radius.circular(5))),
)
],
);
}
void _confirmOrderModalBottomSheet({String totalPrice, String numOfCups}){
showModalBottomSheet(
context: context,
builder: (builder){
return new Container(
height: 150.0,
color: Colors.transparent, //could change this to Color(0xFF737373),
//so you don't have to change MaterialApp canvasColor
child: new Container(
padding: EdgeInsets.all(10),
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0),
topRight: const Radius.circular(10.0))),
child: Column(
children: [
Container(
child: Text("Confirm Order",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),),
alignment: Alignment.center, height: 30, decoration: BoxDecoration(
), ),
_getEstimate(totalPrice, numOfCups)
],
)),
);
}
);
}
Widget _getEstimate(String totalPrice, String numOfCups){
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Image.asset("images/cup_of_coffee.png", height: 70, width: 50,),
Padding(padding: EdgeInsets.all(10)),
Text(numOfCups, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),),
Padding(padding: EdgeInsets.all(10)),
Text(totalPrice, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),)
],
);
}
}
You're setting 1 to your _cupsCounter instead of adding one to it.
When updating your values, putting the operation i.e _cupsCounter += 1; in setState will update the state of your widget which makes values change in your widgets.
setState((){
_cupsCounter += 1; // make it +=1 instead of =1.
price += int.parse(_coffeePrice);
});
Also you can use setState by putting it after your operation which will update the state of your widget after the operation is done.
_cupsCounter += 1; // make it +=1 instead of =1.
price += int.parse(_coffeePrice);
setState((){});
Full code should look like this.
Container(
margin: EdgeInsets.only(top: 30),
padding: EdgeInsets.all(10),
width: double.maxFinite,
height: 70,
child: FlatButton(onPressed: (){
// Currently _cupsCounter only show 1 when this button is clicked.
// Increment the _cupsCounter when 'Add to Bag' button is clicked'
// Call setState((){}) method to update both price and cups counter when 'Add to Bag' button is clicked
setState((){
_cupsCounter += 1; // make it +=1 instead of =1.
price += int.parse(_coffeePrice);
}); // call setState like this
}, child: Center(child: Text("Add to Bag",
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),)
,),
color: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50)
),
),
)