Unexpected null value when try to open drawer in flutter - flutter

I am trying to open the drawer and it gives me the below error:
======== Exception caught by gesture ===============================================================
The following TypeErrorImpl was thrown while handling a gesture:
Unexpected null value.
When the exception was thrown, this was the stack:
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 236:49 throw_
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 518:63 nullCheck
packages/l7/screens/main/view_model/main_view_model.dart 15:36 openOrCloseDrawer
packages/l7/screens/main/view/components/header.dart 35:42 <fn>
packages/flutter/src/material/ink_well.dart 989:21 [_handleTap]
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#ae119
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(35.8, 49.7)
finalLocalPosition: Offset(15.8, 12.9)
button: 1
sent tap down
====================================================================================================
and the below method is openOrCloseDrawer():
void openOrCloseDrawer() {
if (_scaffoldKey.currentState!.isDrawerOpen) {
_scaffoldKey.currentState!.openEndDrawer();
setState(ViewState.Idle);
} else {
_scaffoldKey.currentState!.openDrawer();
setState(ViewState.Idle);
}
}
related to the below ViewModel:
import 'package:flutter/material.dart';
import 'package:l7/enums/ScreenState.dart';
import 'package:l7/screens/BaseViewModel.dart';
class MainViewModel extends BaseViewModel {
int _selectedIndex = 0;
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
int get selectedIndex => _selectedIndex;
List<String> get menuItems =>
["Cases", "Services", "About Us", "Careers", "Blog", "Contact"];
GlobalKey<ScaffoldState> get scaffoldkey => _scaffoldKey;
void openOrCloseDrawer() {
if (_scaffoldKey.currentState!.isDrawerOpen) {
_scaffoldKey.currentState!.openEndDrawer();
setState(ViewState.Idle);
} else {
_scaffoldKey.currentState!.openDrawer();
setState(ViewState.Idle);
}
}
void setMenuIndex(int index) {
_selectedIndex = index;
setState(ViewState.Idle);
}
}
and this is the below BaseViewModel:
import 'package:flutter/widgets.dart';
import 'package:l7/enums/ScreenState.dart';
import 'package:l7/utils/context_extentions.dart';
class BaseViewModel extends ChangeNotifier {
ViewState _state = ViewState.Idle;
ViewState get state => _state;
SwitchState _switchState = SwitchState.CLOSE;
SwitchState get switchState => _switchState;
void setState(ViewState viewState) {
_state = viewState;
notifyListeners();
}
void switchLanguage(bool state, BuildContext context) async {
state == true
? _switchState = SwitchState.OPEN
: _switchState = SwitchState.CLOSE;
notifyListeners();
if (context.locale == const Locale('ar', 'EG')) {
context.setLocale(const Locale('en', 'US'));
} else {
context.setLocale(const Locale('ar', 'EG'));
}
}
}
and this is the below Drawer component:
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:l7/screens/BaseScreen.dart';
import 'package:l7/screens/main/view_model/main_view_model.dart';
import 'package:l7/utils/constants.dart';
class SideMenu extends StatelessWidget {
// final MenuController _controller = Get.put(MenuController());
#override
Widget build(BuildContext context) {
return BaseScreen<MainViewModel>(
onModelReady: (mainViewModel){},
builder: (context, viewModel, _){
return Drawer(
child: Container(
color: kDarkBlackColor,
child: ListView(
children: [
DrawerHeader(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: kDefaultPadding * 3.5),
child: SvgPicture.asset("assets/icons/logo.svg"),
),
),
...List.generate(
viewModel.menuItems.length,
(index) => DrawerItem(
isActive: index == viewModel.selectedIndex,
title: viewModel.menuItems[index],
press: () {
viewModel.setMenuIndex(index);
},
),
),
],
),
),
);
},
);
}
}
class DrawerItem extends StatelessWidget {
final String? title;
final bool? isActive;
final VoidCallback? press;
const DrawerItem({
Key? key,
#required this.title,
#required this.isActive,
#required this.press,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return SafeArea(
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: kDefaultPadding),
selected: isActive!,
selectedTileColor: kPrimaryColor,
onTap: press,
title: Text(
title!,
style: TextStyle(color: Colors.white),
),
),
);
}
}
and this is the Header Widget which contains the drawer:
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:l7/screens/BaseScreen.dart';
import 'package:l7/screens/main/view/components/web_menu.dart';
import 'package:l7/screens/main/view_model/main_view_model.dart';
import 'package:l7/services/responsive.dart';
import 'package:l7/utils/constants.dart';
import 'package:l7/utils/texts.dart';
import 'header_right_side.dart';
class Header extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BaseScreen<MainViewModel>(
onModelReady: (mainViewModel) {},
builder: (context, viewModel, _) {
return SafeArea(
child: Column(
children: [
Column(
children: [
Row(
children: [
if (!Responsive.isDesktop(context))
IconButton(
icon: Icon(
Icons.menu,
color: kBlackBlue,
),
onPressed: () {
viewModel.openOrCloseDrawer();
},
),
Image.asset("assets/images/l7_image.png", height: MediaQuery.of(context).size.height * 0.15,),
Spacer(),
if (Responsive.isDesktop(context)) WebMenu(),
Spacer(),
HeaderRightSide(),
],
),
// SizedBox(height: kDefaultPadding * 2),
Container(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.27,
decoration: BoxDecoration(
image: DecorationImage(image: AssetImage('assets/images/blog_bg.png'), fit: BoxFit.cover)
),
child: Row(
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
headLine30TitleText(
tr("Welcome to Our Blog"),
context,
),
Padding(
padding:
const EdgeInsets.symmetric(vertical: kDefaultPadding),
child: Text(
"Stay updated with the newest design and development stories, case studies, \nand insights shared by DesignDK Team.",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontFamily: 'Raleway',
height: 1.5,
),
),
),
FittedBox(
child: TextButton(
onPressed: () {},
child: Row(
children: [
Text(
"Learn More",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(width: kDefaultPadding / 2),
Icon(
Icons.arrow_forward,
color: Colors.white,
),
],
),
),
),
],
),
),
IconButton(onPressed: (){}, icon: Icon(Icons.keyboard_arrow_right, color: Colors.white,))
],
),
),
if (Responsive.isDesktop(context))
SizedBox(height: kDefaultPadding),
],
)
],
),
);
},
);
}
}
I hope someone could help, and let me know if there's any missing details or code. :)

The solution in the below line code in Header widget:
instead of:
viewModel.openOrCloseDrawer();
add this line:
Scaffold.of(context).openDrawer();

Related

How to move the Quiz interface to the next question?

Here is the code for a Quiz App I have been making. This is the main.dart file.
import 'package:flutter/material.dart';
import 'list.dart';
void main() {
runApp(
const Quizzler(),
);
}
class Quizzler extends StatefulWidget {
const Quizzler({Key? key}) : super(key: key);
#override
State<Quizzler> createState() => _QuizzlerState();
}
class _QuizzlerState extends State<Quizzler> {
List<Widget> scoreKeeper = [];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black,
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
flex: 6,
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Center(
child: Text(
Quiz().getQuestion(),
style: const TextStyle(
color: Colors.white,
fontSize: 20,
),
textAlign: TextAlign.center,
),
),
),
),
boolButton(Colors.green, 'True'),
boolButton(Colors.red, 'False'),
Row(
children: scoreKeeper,
),
],
),
),
);
}
Expanded boolButton(Color c, String s) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(c),
),
onPressed: () {
setState(
() {
switcher(s);
Quiz().nextQuestion();
},
);
},
child: Text(
s,
),
),
),
);
}
switcher(String s) {
if (Quiz().getAnswer().toLowerCase() == s.toLowerCase()) {
scoreKeeper.add(
const Icon(
Icons.check,
color: Colors.green,
),
);
} else {
scoreKeeper.add(
const Icon(
Icons.close,
color: Colors.red,
),
);
}
}
}
This is the list.dart file that I imported onto the main.dart file:
import 'question.dart';
class Quiz {
int _counter = 0;
final List<Question> _q = [
Question(
'Cows can only be lead up a flight of stairs and not down.',
'False',
),
Question(
'Approximately one quarter of human bones are in the feet.',
'True',
),
Question(
'A slug\'s blood is green.',
'True',
),
];
void nextQuestion() {
_counter++;
}
String getQuestion() {
return _q[_counter].question;
}
String getAnswer() {
return _q[_counter].answer;
}
}
And this is the question.dart file that I imported onto the list.dart file:
class Question {
String question;
String answer;
Question(this.question, this.answer);
}
In the Quiz app, once I hit True or False it just shows a tick for the right answer (False) and a cross for the wrong answer (True), without moving to the next question. How do I fix this?
Here, you call any variables or methods from Quiz class by calling directly by Quiz(), basically problems occur here.
By calling Quiz() every time you created a new object, there is no tracking of this object. Because you don't save the object.
So, at first, save the object like that...
Quiz quiz = Quiz();
then wherever you call by Quiz(), replace by quiz.
be like..... quiz.getQuestion(),quiz.nextQuestion(),quiz.getAnswer()
I add all three dart file added below ... with the solution of showing a ques ending alert snackbar.
This is the main.dart file.
import 'package:flutter/material.dart';
import 'list.dart';
void main() {
runApp(
const Quizzler(),
);
}
class Quizzler extends StatefulWidget {
const Quizzler({Key? key}) : super(key: key);
#override
State<Quizzler> createState() => _QuizzlerState();
}
class _QuizzlerState extends State<Quizzler> {
/// This is for show snack bar
final _messangerKey = GlobalKey<ScaffoldMessengerState>();
Quiz quiz = Quiz();
List<Widget> scoreKeeper = [];
#override
Widget build(BuildContext context) {
return MaterialApp(
scaffoldMessengerKey: _messangerKey,
home: Scaffold(
backgroundColor: Colors.black,
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
flex: 6,
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Center(
child: Text(
quiz.getQuestion(),
style: const TextStyle(
color: Colors.white,
fontSize: 20,
),
textAlign: TextAlign.center,
),
),
),
),
boolButton(Colors.green, 'True'),
boolButton(Colors.red, 'False'),
Row(
children: scoreKeeper,
),
],
),
),
);
}
Expanded boolButton(Color c, String s) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(c),
),
onPressed: () {
(quiz.q.length - 1) > quiz.counter ? setState(
() {
switcher(s);
quiz.nextQuestion();
},
) : _messangerKey.currentState!.showSnackBar(
const SnackBar(duration: Duration(seconds : 2),content: Text('No more questions')));
},
child: Text(
s,
),
),
),
);
}
switcher(String s) {
if (quiz.getAnswer().toLowerCase() == s.toLowerCase()) {
scoreKeeper.add(
const Icon(
Icons.check,
color: Colors.green,
),
);
} else {
scoreKeeper.add(
const Icon(
Icons.close,
color: Colors.red,
),
);
}
}
}
This is the list.dart file
import 'question.dart';
class Quiz {
int counter = 0;
final List<Question> q = [
Question(
'Cows can only be lead up a flight of stairs and not down.',
'False',
),
Question(
'Approximately one quarter of human bones are in the feet.',
'True',
),
Question(
'A slug\'s blood is green.',
'True',
),
];
void nextQuestion() {
counter++;
}
String getQuestion() {
return q[counter].question;
}
String getAnswer() {
return q[counter].answer;
}
}
And this is the question.dart file
class Question {
String question;
String answer;
Question(this.question, this.answer);
}

type '_CompactLinkedHashSet<Question>' is not a subtype of type 'List<Question>' in type cast

I am facing this error it runs perfectly but it show no output and display the
following error
type '_CompactLinkedHashSet' is not a subtype of type 'List' in type cast
Flutter
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:quizler/Question.dart';
import 'Question.dart';
void main() => runApp(quizler());
class quizler extends StatelessWidget {
const quizler({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.grey.shade900,
body: SafeArea(
child: QuizzPage(),
),
),
);
}
}
class QuizzPage extends StatefulWidget {
const QuizzPage({Key? key}) : super(key: key);
#override
_QuizzPageState createState() => _QuizzPageState();
}
class _QuizzPageState extends State<QuizzPage> {
List<Widget> scoreKeeper = [];
List<Question> questionBank={
Question(q: 'Moon Color is gray',a:false),
Question(q: 'Independence Day of pakistan was 1947',a:true),
Question(q: 'Tesla was the owner of Starlink company',a:false),
} as List<Question> ;
int questionNumber = 0;
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
flex: 5,
child: Padding(
padding: EdgeInsets.all(15.0),
child: Center(
child: Text(
questionBank[questionNumber].questionText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25.0,
color: Colors.white,
),
),
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.all(20.0),
child: FlatButton(
textColor: Colors.white,
color: Colors.green,
onPressed: () {
bool correctAnswer = questionBank[questionNumber].questionAnswer;
if (correctAnswer == true) {
setState(() {
questionNumber++;
scoreKeeper.add(
Icon(Icons.check, color: Colors.green),
);
});
} else {
setState(() {
questionNumber++;
scoreKeeper.add(
Icon(
Icons.check,
color: Colors.red,
),
);
});
}
},
child: Text(
'True',
),
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.all(20.0),
child: FlatButton(
textColor: Colors.white,
color: Colors.red,
onPressed: () {
bool correctAnswer = questionBank[questionNumber].questionAnswer;
if (correctAnswer == false) {
setState(
() {
questionNumber++;
scoreKeeper.add(
Icon(Icons.check, color: Colors.green),
);
},);
}
else{
setState(
() {
questionNumber++;
scoreKeeper.add(
Icon(Icons.check, color: Colors.red),
);
},);
}
},
child: Text(
'False',
),
),
),
),
Row(
children: scoreKeeper,
),
],
);
}
}
**Here is the Question.dart code**
> Question.dart
class Question{ late String questionText; late bool
questionAnswer; Question({required String q, required bool a}) {
questionText=q; questionAnswer=a; } }'
The following _CastError was thrown building MediaQuery(MediaQueryData(size: Size(360.0, 736.0), devicePixelRatio: 3.0, textScaleFactor: 1.0, platformBrightness: Brightness.dark, padding: EdgeInsets.zero, viewPadding: EdgeInsets.zero, viewInsets: EdgeInsets.zero, alwaysUse24HourFormat: false, accessibleNavigation: false, highContrast: false, disableAnimations: false, invertColors: false, boldText: false, navigationMode: traditional)):
type '_CompactLinkedHashSet' is not a subtype of type 'List' in type cast
Change it to this:
List<Question> questionBank=[
Question(q: 'Moon Color is gray',a:false),
Question(q: 'Independence Day of pakistan was 1947',a:true),
Question(q: 'Tesla was the owner of Starlink company',a:false),
]
Use square brackets. Or change your definition to:
Set<Question> questionBank = { ....etc };

Flutter : The onRefresh callback returned null

Sorry I'm new to flutter and trying to learn it. I have an issue with RefreshIndicator.
When I try pulling it I got an error like below :
════════ Exception caught by material library ══════════════════════════════════════════════════════
The following assertion was thrown when calling onRefresh:
The onRefresh callback returned null.
The RefreshIndicator onRefresh callback must return a Future.
════════════════════════════════════════════════════════════════════════════════════════════════════
Currently I am using flutter_bloc. Here is my sample code
TableList_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:pos_project_bloc/modal/api.dart';
class ModalTableList {
final String Table_Number;
final String Name_Table;
ModalTableList(
this.Table_Number,
this.Name_Table,
);
}
class tablelistbloc extends Bloc<bool, List<ModalTableList>>{
#override
// TODO: implement initialState
List<ModalTableList> get initialState => [];
#override
Stream<List<ModalTableList>> mapEventToState(bool event) async* {
// TODO: implement mapEventToState
List<ModalTableList> tablelist =[];
try {
final response = await http.get(BaseUrl.GetTableList);
final data = jsonDecode(response.body);
if (data.length != 0) {
data.forEach((api) {
tablelist.add(
ModalTableList(
api['Table_Number'],
api['Name_Table'],
)
);
print("test"+api['Table_Number'].toString());
});
print("Get Table List : sukses");
} else {
print('data kosong');
}
} catch (e) {
print("Error GetTableList :");
print(e);
}
yield tablelist;
}
}
TabeList.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pos_project_bloc/bloc/TableList_bloc.dart';
import 'package:pos_project_bloc/modal/SharedPreferences.dart';
class TableList extends StatefulWidget {
#override
_TableListState createState() => _TableListState();
}
class _TableListState extends State<TableList> {
#override
void initState() {
super.initState();
BlocProvider.of<tablelistbloc>(context).add(true);
}
Widget build(BuildContext context) {
final GlobalKey<RefreshIndicatorState> refresh = GlobalKey<RefreshIndicatorState>();
Future<Null> _refresh() async{
BlocProvider.of<tablelistbloc>(context).add(true);
}
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.lightBlueAccent,
title: new Center(
child: new Text('Available Table',
style: new TextStyle(color: Colors.white, fontSize: 15.0)),
)
),
floatingActionButton: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.all(5.0),
child: FloatingActionButton.extended(
heroTag: 1,
icon: Icon(Icons.refresh),
label: Text('Refresh Table List'),
onPressed: () {
BlocProvider.of<tablelistbloc>(context).add(true);
},
),
)
],
),
),
body: RefreshIndicator(
onRefresh: (){
_refresh();
},
key: refresh,
child: BlocBuilder<tablelistbloc,List<ModalTableList>>(
builder: (context,tablelist)=> ListView.builder(
itemCount: tablelist.length,
itemBuilder: (context,index){
final x = tablelist[index];
return GestureDetector(
onTap: (){
print("Selected Available Table On Tap : " + x.Table_Number);
Shared_Preferences().SaveTableNumber(
x.Table_Number
);
Navigator.pushReplacementNamed(context, '/POS');
},
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 2.0,
color: Colors.grey.withOpacity(0.4),
))),
padding: EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
Icon(
Icons.table_chart,
size: 100.0,
color: Colors.lightBlueAccent,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Table Number : ' + x.Table_Number,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.lightBlueAccent),
),
Text(
'Name : ' + x.Name_Table,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.lightBlueAccent),
),
],
)
],
),
),
);
},
),
),
)
);
}
}
Just add async.
onRefresh: () async {
GetTableList.add(true);
},
onRefresh is a RefreshCallback, and RefreshCallback is a Future Function().
So if GetTableList.add(true) not return Future, must to add async.
it is says onRefresh callback must return a Future.
and it is seems like your are not returning Future from onRefresh
onRefresh: _refresh
Future<Null> _refresh() async{
GetTableList.add(true);
}
hope it helps..
You can problably use the following:
Future<bool> refresh() async {
//your refresh code
return true;
}

having an problem designing Provider design to flutter application

The main reason i want to change the design is that when i scan a product and then scan the same one i get the same product twice while it is the same one so basically i just need to increase the quantity of the product by one.
I have used Provider before but I still can't manage to arrange the classes the right way to make it work.
first i have tried to make a Productobject(which extenes ChangeNotifier) inside ProductCard class and use provider.of(context) and retrieve the quantity of the prodcut.
plus,at the main before the material I used ChangeNotifierProvider
I have a mess in my head and i would love to see someone arranging the code with explanation of how the things works
main
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sally_smart/utilities/product_notifier.dart';
import 'screens/checkout_screen.dart';
import 'screens/login_screen.dart';
import 'screens/registration_screen.dart';
import 'screens/welcome_screen.dart';
import 'package:provider/provider.dart';
//void main() => runApp(Sally());
void main() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) {
runApp(new Sally());
});
}
class Sally extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
backgroundColor: Colors.teal,
cardColor: Color(0xFF068194),
),
initialRoute: LoginScreen.id,
routes: {
WelcomeScreen.id: (context) => WelcomeScreen(),
RegistrationScreen.id: (context) => RegistrationScreen(),
LoginScreen.id: (context) => LoginScreen(),
CheckoutScreen.id: (context) => CheckoutScreen()
},
);
}
}
ProductCard
import 'package:flutter/material.dart';
import 'package:sally_smart/utilities/constants.dart';
import 'package:sally_smart/utilities/product.dart';
import 'package:sally_smart/utilities/round_icon_button.dart';
class ProductCard extends StatefulWidget {
final Product prodcut;
ProductCard(Product product);
#override
_ProductCardState createState() => _ProductCardState();
}
class _ProductCardState extends State<ProductCard> {
double finalPrice;
// static int quantity = 1;
#override
Widget build(BuildContext context) {
finalPrice = widget.prodcut.quantity * widget.prodcut.productPrice;
return Card(
elevation: 5.0,
child: ListTile(
leading: Padding(
padding: EdgeInsets.only(
left: 2.0,
),
child: Icon(
widget.prodcut.productIcon,
size: 35,
),
),
title: Text(
widget.prodcut.productName,
style: kProductNameTextStyle,
),
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
RoundIconButton(
icon: Icons.add,
color: Colors.green,
function: () {
setState(() {
widget.prodcut.quantity++;
});
}),
Text(
'$widget.prodcut.quantity',
),
RoundIconButton(
icon: Icons.remove,
color: Colors.red,
function: () {
setState(() {
widget.prodcut.quantity--;
if ( widget.prodcut.quantity == 0) {
widget.prodcut.quantity++;
}
});
}),
],
),
),
Text(
'${finalPrice.toStringAsFixed(2)} ₪',
style: TextStyle(fontSize: 15),
),
],
),
),
);
}
}
Prodcut
import 'package:flutter/cupertino.dart';
class Product extends ChangeNotifier{
String productName;
double productPrice;
IconData productIcon;
String id;
String barCode;
int quantity;
Product(this.productName, this.productPrice, this.productIcon, this.id,
this.barCode,this.quantity);
}
welcome screen
import 'package:audioplayers/audio_cache.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
import 'package:sally_smart/screens/login_screen.dart';
import 'package:sally_smart/screens/registration_screen.dart';
import 'package:sally_smart/utilities/constants.dart';
import 'package:sally_smart/utilities/product.dart';
import 'package:sally_smart/utilities/product_card.dart';
import 'package:sally_smart/utilities/scan_button_const.dart';
import 'package:sally_smart/utilities/scan_methods.dart';
//import 'package:sally_smart/utilities/scan_pageML.dart';
//import 'package:flutter_camera_ml_vision/flutter_camera_ml_vision.dart';
//import 'package:firebase_ml_vision/firebase_ml_vision.dart';
//working version
//List<ProductCard> shoppingList = [];
final sallyDatabase = Firestore.instance;
class WelcomeScreen extends StatefulWidget {
static const String id = 'welcome_screen';
#override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> {
final _auth = FirebaseAuth.instance;
final textEditorController = TextEditingController();
String _scanBarcode = 'Unknown';
String productName = 'Product Test';
double productPrice;
String productBarCode;
IconData productIcon = Icons.add_shopping_cart;
final List<ProductCard> shoppingList = [];
int productId = 0;
static AudioCache barcodeSound = AudioCache();
// saves barcodes data
List<String> data = [];
Future<void> initPlatformState() async {
String barcodeScanRes;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
barcodeScanRes =
await FlutterBarcodeScanner.scanBarcode("#ff6666", "Cancel", true);
} on PlatformException {
barcodeScanRes = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_scanBarcode = barcodeScanRes;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF21bacf),
appBar: AppBar(
elevation: 3,
backgroundColor: Colors.black54,
leading: Icon(
Icons.shopping_basket,
size: 30,
),
title: Text(
'Sally',
textAlign: TextAlign.end,
style: kHeaderTextStyle,
),
actions: <Widget>[
IconButton(icon: Icon(Icons.settings), onPressed: () {}),
VerticalDivider(
color: Color(0x8CFFFFFF),
width: 3,
),
IconButton(
icon: Icon(Icons.power_settings_new),
onPressed: () {
_auth.signOut();
Navigator.pushNamed(context, LoginScreen.id);
}),
VerticalDivider(
color: Color(0x8CFFFFFF),
width: 3,
),
IconButton(icon: Icon(Icons.share), onPressed: () {}),
VerticalDivider(
color: Color(0x8CFFFFFF),
width: 3,
),
// PopupMenuButton(itemBuilder: ),
],
),
body: Container(
decoration: kBackgroundGradientScan,
child: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text(
'ברוכה הבאה, סאלי',
textAlign: TextAlign.right,
style: kHeaderTextStyle,
),
Padding(
padding: EdgeInsets.only(top: 5, right: 15),
child: Hero(
tag: 'Sally',
child: CircleAvatar(
backgroundImage:
AssetImage('images/missing_avatar_F.png'),
maxRadius: 25,
),
),
)
],
),
Padding(
padding: EdgeInsets.symmetric(vertical: 3, horizontal: 15),
child: TextField(
textAlign: TextAlign.center,
onChanged: (value) async {
_scanBarcode = value;
productPrice = await getProductPrice(_scanBarcode);
productName = await getProductName(_scanBarcode);
},
decoration: kTextFieldDecoration.copyWith(
prefixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: () {
_scanBarcode = '';
textEditorController.clear();
try {
setState(() {
textEditorController.clear();
//checking if a product was already scanned
//adding a ProductCard to the shopping list with the ProductCard const. Works on scan
shoppingList.add(ProductCard( new Product(productName, productPrice, productIcon, shoppingList.length.toString(),
productBarCode, 1)));
});
} catch (e) {
print(e);
}
},
),
// prefix: IconButton(
// icon: Icon(Icons.search),
// onPressed: () {
//
// }),
hintText: '...הכנס ברקוד או שם מוצר ידנית'),
),
),
DividerSally(),
shoppingListBuilder(),
DividerSally(),
Container(
child: Padding(
padding: EdgeInsets.only(bottom: 15.0),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 40),
child: Column(
children: <Widget>[
ScanMainButton(
iconData: Icons.flip,
buttonText: 'סרוק מוצר',
onPressed: () async {
//Navigator.pushNamed(context, ScanScreen.id);
await initPlatformState();
barcodeSound.play('barcode_sound.mp3');
//Changes the product name by referencing to the database
productBarCode = _scanBarcode;
productPrice =
await getProductPrice(_scanBarcode);
productName =
await getProductName(_scanBarcode);
setState(() {
//checking if a product was already scanned
//adding a ProductCard to the shopping list with the ProductCard const. Works on scan
shoppingList.add(ProductCard( new Product(productName, productPrice, productIcon, shoppingList.length.toString(),
productBarCode, 1)));
});
},
color: Colors.teal,
),
ScanMainButton(
iconData: Icons.check,
buttonText: 'מעבר לתשלום',
color: Colors.green,
onPressed: () {
Navigator.pushNamed(
context, RegistrationScreen.id);
}),
],
),
),
),
)
],
),
),
),
));
}
Expanded shoppingListBuilder() {
return Expanded(
child: Container(
color: Colors.black38,
child: ListView.builder(
reverse: true,
itemCount: shoppingList.length,
itemBuilder: (context, index) {
ProductCard item = shoppingList[index];
return Dismissible(
key: Key(item.prodcut.id),
direction: DismissDirection.startToEnd,
onDismissed: (direction) {
setState(() {
shoppingList.removeAt(index);
});
},
background: Container(
child: Icon(
Icons.restore_from_trash,
size: 40,
),
margin: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomLeft,
end: Alignment.topRight,
colors: [
Color(0x8C650223),
Color(0x8CB9013E),
],
stops: [0.1, 0.9],
),
),
),
child: item //ListTile(title: Text('${item.productName}.')),
);
},
),
));
}
}

How to pass data back from a widget?

I have a screen where users can add a location. Here, I have separated all my widgets into there own files as illustrated below;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/location/location_help_screen.dart';
import 'package:fluttershare/widgets/common_widgets/customDivider.dart';
import 'package:uuid/uuid.dart';
import '../../widgets/camp_type_select.dart';
import '../../widgets/extra_location_notes.dart';
import '../../widgets/location_input.dart';
import '../../widgets/opening_times.dart';
import '../../widgets/post_media.dart';
import '../../widgets/space_avalibility.dart';
import '../../widgets/utility_type_select.dart';
import '../../widgets/width_restriction.dart';
import '../../widgets/height_restriction.dart';
import '../../models/locations.dart';
import '../../models/user.dart';
import '../home.dart';
class AddNewLocation extends StatefulWidget {
static const routeName = '/add-new-location';
final User currentUser;
AddNewLocation({this.currentUser});
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
String postId = Uuid().v4();
final _scaffoldKey = GlobalKey<ScaffoldState>();
PlaceLocation _pickedLocation;
int storyPostCount = 0;
bool isLoading = false;
void _selectPlace(double lat, double lng) {
_pickedLocation = PlaceLocation(lattitude: lat, longitude: lng);
}
getLocationPostCount() async {
setState(() {
isLoading = true;
});
QuerySnapshot snapshot = await locationPostRef
.document(currentUser.id)
.collection('user_location_posts')
.getDocuments();
setState(() {
storyPostCount = snapshot.documents.length;
});
}
createLocationPostInFirestore(
{String mediaUrl,
String description,
double heightRestriction,
double widthRestriction}) {
locationPostRef
.document(currentUser.id)
.collection("user_location_posts")
.document(postId)
.setData({
"postId": postId,
"ownerId": currentUser.id,
"username": currentUser.username,
"description": description,
"timestamp": timestamp,
"lattitude": _pickedLocation.lattitude,
"longitude": _pickedLocation.longitude,
"max_height": heightRestrictionValue.toStringAsFixed(0),
"max_width": widthRestrictionValue.toStringAsFixed(0),
});
}
handlePostSubmit() {
createLocationPostInFirestore(
heightRestriction: heightRestrictionValue,
widthRestriction: widthRestrictionValue,
);
SnackBar snackbar = SnackBar(
content: Text("Profile Updated"),
);
_scaffoldKey.currentState.showSnackBar(snackbar);
setState(() {
postId = Uuid().v4();
});
}
buildUploadUserHeader() {
return Container(
margin: EdgeInsets.only(bottom: 10),
height: 200,
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListTile(
leading: CircleAvatar(
backgroundImage:
CachedNetworkImageProvider(currentUser.photoUrl)),
),
],
),
),
),
Expanded(
flex: 6,
child: Container(
color: Colors.pink,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text(currentUser.displayName),
],
),
),
),
],
),
);
}
buildCampUploadForm() {
return Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
//buildUploadUserHeader(), //TODO: This is the profile header that is dissabled for now. Work on possibly a header in the future.
Container(
padding: EdgeInsets.all(15),
child: Column(
children: <Widget>[
CampTypeSelect(),
CustomDivider(),
LocationInput(_selectPlace),
CustomDivider(),
HeightRestriction(),
WidthRestriction(),
SpaceAvalibility(),
OpeningTimes(),
CustomDivider(),
PostMedia(),
CustomDivider(),
UtilityServices(),
CustomDivider(),
ExtraLocationNotes(),
Container(
height: 80,
margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
children: <Widget>[
Expanded(
child: FlatButton(
color: Colors.black,
onPressed: () => handlePostSubmit(),
child: Text(
"SUBMIT",
style: Theme.of(context).textTheme.display2,
),
padding: EdgeInsets.all(20),
),
)
],
),
),
],
),
),
],
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text(
'Add New Location',
style: TextStyle(color: Colors.black),
),
actions: <Widget>[
// action button
IconButton(
icon: Icon(Icons.info_outline),
color: Colors.black,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) => LocationSubmitHelpScreen()),
);
},
),
// action button
IconButton(
icon: Icon(Icons.close),
color: Colors.black,
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
body: buildCampUploadForm(),
backgroundColor: Colors.white,
);
}
}
What I am trying to do is pass the data back from the widget ExtraLocationNotes()
to the function createLocationPostInFirestore().
For context, this is what my widget looks like;
import 'package:flutter/material.dart';
import 'common_widgets/custom_form_card.dart';
class ExtraLocationNotes extends StatefulWidget {
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
TextEditingController descriptionController = TextEditingController();
#override
Widget build(BuildContext context) {
return CustomFormCard(
child: Column(
children: <Widget>[
Container(
child: Row(
children: <Widget>[
Text(
"EXTRA INFORMATION",
style: TextStyle(
fontSize: 18.0,
color: Colors.black,
fontWeight: FontWeight.w400,
letterSpacing: 2.0,
),
),
],
),
),
SizedBox(height: 20),
TextFormField(
controller: descriptionController,
maxLines: 6,
maxLength: 250,
maxLengthEnforced: true,
style:
new TextStyle(fontSize: 18.0, height: 1.3, color: Colors.black),
decoration: const InputDecoration(
hintText:
"Please write a description of this location for fellow travellers.",
alignLabelWithHint: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.only(),
borderSide: BorderSide(color: Colors.black),
),
),
),
],
),
);
}
}
How do I pass the data back to the parent widget?
You need a callback, which will be triggered in the child widget then the value will be updated in the parent widget:
// 1- Define a pointers to executable code in memory, which is the callback.
typedef void MyCallback(String val);
class ExtraLocationNotes extends StatefulWidget {
// 2- You will pass it to this widget with the constructor.
final MyCallback cb;
// 3- ..pass it to this widget with the constructor
ExtraLocationNotes({this.cb});
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
//..
//...
RaisedButton(
//..
// 4- in any event inside the child you can call the callback with
// the data you want to send back to the parent widget:
onPressed: () {
widget.cb("Hello from the other side!");
}
),
}
Then inside the parent widget you need to catch the data which sent form the child:
class AddNewLocation extends StatefulWidget {
//...
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
// 1- Global var to store the data that we're waiting for.
String _dataFromMyChild = "";
buildCampUploadForm() {
return Container(
//...
//...
// 2- Pass the callback with the constructor of the child, this
// will update _dataFromMyChild's value:
ExtraLocationNotes(cb: (v) => setState(() => _dataFromMyChild = v)),
//..
}
// then
createLocationPostInFirestore() {
// Use _dataFromMyChild's value here
}
}
You can use the BuildContext object to get the context widget (might no be the parent!) couldn't read it all but as i understand that you need to pass the info from the child to the parent ,and you can do it with some like this :-
(context.widget as MyType).doStuff();
Note.
please check first with
print(context.widget.runtimeType);
but to make a better solution make a mutable data object that is passed from parent to the child so when changes happens it reflect's on the parent so you can separate business logic from ui logic.