Access Provider variable in child widget methods - flutter

I'm trying to access branchSelected, a Provider declared towards the top of my code in the scaffold's child widgets. When I use branchSelected.branchDropdownValue in the original widget, it works. However, when I go into the child widget methods, specifically
ElevatedButton(
onPressed: () {
branchSelected.branchChange;
/*
refreshBranchData();
refreshProjectData();
*/
},
child: Icon(Icons.refresh))
, I received the error "Undefined name 'branchSelected'." Looking at other documentation, I thought that all child widgets should be able to access the Provider; however, that doesn't seem to be the case here. Any ideas? I've attached my code below:
#override
Widget build(BuildContext context) {
final branchSelected = Provider.of<branchListChanges>(context);
return Scaffold(
appBar: AppBar(
title: Text('Build Launcher'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
_buildTopRow(),
Consumer<branchListChanges>(builder: (context, data, child) {
return Text(
'Current Branch: ${branchSelected.branchDropdownValue}');
}),
_buildList(),
],
),
),
backgroundColor: Colors.grey[200],
);
}
Expanded _buildList() {
// Sets which branch to view for the list runs
return Expanded(
child: RunList(branches, "main"),
);
}
Row _buildTopRow() {
return Row(
children: [
Text("Project:"),
SizedBox(width: 6),
ProjectDropdown(packages),
SizedBox(width: 6),
Text("Branch:"),
SizedBox(width: 6),
BranchDropdown(branchNames),
SizedBox(width: 6),
Checkbox(
checkColor: Colors.white,
fillColor: MaterialStateProperty.all<Color>(Colors.blue),
value: onlyLatestPerDay,
onChanged: (bool? value) {
setState(() {
onlyLatestPerDay = value!;
});
},
),
SizedBox(width: 3),
Text("Only latest per-day"),
SizedBox(width: 6),
Checkbox(
checkColor: Colors.white,
fillColor: MaterialStateProperty.all<Color>(Colors.blue),
value: onlyInstalled,
onChanged: (bool? value) {
setState(() {
onlyInstalled = value!;
});
},
),
SizedBox(width: 3),
Text("Only installed"),
SizedBox(width: 6),
ElevatedButton(
onPressed: () {
branchSelected.branchChange;
/*
refreshBranchData();
refreshProjectData();
*/
},
child: Icon(Icons.refresh))
],
);
}
}
I created my Provider in main.dart attached below:
class HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'cornerCan',
theme: ThemeData(
primarySwatch: Colors.blue,
),
// EFFECTS: runs mainPage function in main_page.dart
home: ChangeNotifierProvider<branchListChanges>(
create: (context) => branchListChanges(),
child: mainPage(),
),
);
}
}

The scope of your branchSelected variable is only inside the build function. You need to declare it outside the build function to access it.
You can also pass it as a parameter to the _buildTopRow() function
e.g. _buildTopRow(branchSelected)

Related

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

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

Passing value to previous widget

I have simple form , inside it have CircularAvatar when this is pressed show ModalBottomSheet to choose between take picture from gallery or camera. To make my widget more compact , i separated it to some file.
FormDosenScreen (It's main screen)
DosenImagePicker (It's only CircularAvatar)
ModalBottomSheetPickImage (It's to show ModalBottomSheet)
The problem is , i don't know how to passing value from ModalBottomSheetPickImage to FormDosenScreen. Because value from ModalBottomSheetPickImage i will use to insert operation.
I only success passing from third Widget to second Widget , but when i passing again from second Widget to first widget the value is null, and i think the problem is passing from Second widget to first widget.
How can i passing from third Widget to first Widget ?
First Widget
class FormDosenScreen extends StatefulWidget {
static const routeNamed = '/formdosen-screen';
#override
_FormDosenScreenState createState() => _FormDosenScreenState();
}
class _FormDosenScreenState extends State<FormDosenScreen> {
String selectedFile;
#override
Widget build(BuildContext context) {
final detectKeyboardOpen = MediaQuery.of(context).viewInsets.bottom;
print('trigger');
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Tambah Dosen'),
actions: <Widget>[
PopupMenuButton(
itemBuilder: (_) => [
PopupMenuItem(
child: Text('Tambah Pelajaran'),
value: 'add_pelajaran',
),
],
onSelected: (String value) {
switch (value) {
case 'add_pelajaran':
Navigator.of(context).pushNamed(FormPelajaranScreen.routeNamed);
break;
default:
}
},
)
],
),
body: Stack(
fit: StackFit.expand,
children: <Widget>[
SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(height: 20),
DosenImagePicker(onPickedImage: (file) => selectedFile = file),
SizedBox(height: 20),
Card(
margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormFieldCustom(
onSaved: (value) {},
labelText: 'Nama Dosen',
),
SizedBox(height: 20),
TextFormFieldCustom(
onSaved: (value) {},
prefixIcon: Icon(Icons.email),
labelText: 'Email Dosen',
keyboardType: TextInputType.emailAddress,
),
SizedBox(height: 20),
TextFormFieldCustom(
onSaved: (value) {},
keyboardType: TextInputType.number,
inputFormatter: [
// InputNumberFormat(),
WhitelistingTextInputFormatter.digitsOnly
],
prefixIcon: Icon(Icons.local_phone),
labelText: 'Telepon Dosen',
),
],
),
),
),
SizedBox(height: kToolbarHeight),
],
),
),
Positioned(
child: Visibility(
visible: detectKeyboardOpen > 0 ? false : true,
child: RaisedButton(
onPressed: () {
print(selectedFile);
},
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
color: colorPallete.primaryColor,
child: Text(
'SIMPAN',
style: TextStyle(fontWeight: FontWeight.bold, fontFamily: AppConfig.headerFont),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
textTheme: ButtonTextTheme.primary,
),
),
bottom: kToolbarHeight / 2,
left: sizes.width(context) / 15,
right: sizes.width(context) / 15,
)
],
),
);
}
}
Second Widget
class DosenImagePicker extends StatefulWidget {
final Function(String file) onPickedImage;
DosenImagePicker({#required this.onPickedImage});
#override
DosenImagePickerState createState() => DosenImagePickerState();
}
class DosenImagePickerState extends State<DosenImagePicker> {
String selectedImage;
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: InkWell(
onTap: () async {
await showModalBottomSheet(
context: context,
builder: (context) => ModalBottomSheetPickImage(
onPickedImage: (file) {
setState(() {
selectedImage = file;
widget.onPickedImage(selectedImage);
print('Hellooo dosen image picker $selectedImage');
});
},
),
);
},
child: CircleAvatar(
foregroundColor: colorPallete.black,
backgroundImage: selectedImage == null ? null : MemoryImage(base64.decode(selectedImage)),
radius: sizes.width(context) / 6,
backgroundColor: colorPallete.accentColor,
child: selectedImage == null ? Text('Pilih Gambar') : SizedBox(),
),
),
);
}
}
Third Widget
class ModalBottomSheetPickImage extends StatelessWidget {
final Function(String file) onPickedImage;
ModalBottomSheetPickImage({#required this.onPickedImage});
#override
Widget build(BuildContext context) {
return SizedBox(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Wrap(
alignment: WrapAlignment.spaceEvenly,
children: <Widget>[
InkWell(
onTap: () async {
final String resultBase64 =
await commonFunction.pickImage(quality: 80, returnFile: ReturnFile.BASE64);
onPickedImage(resultBase64);
},
child: CircleAvatar(
foregroundColor: colorPallete.white,
backgroundColor: colorPallete.green,
child: Icon(Icons.camera_alt),
),
),
InkWell(
onTap: () async {
final String resultBase64 =
await commonFunction.pickImage(returnFile: ReturnFile.BASE64, isCamera: false);
onPickedImage(resultBase64);
},
child: CircleAvatar(
foregroundColor: colorPallete.white,
backgroundColor: colorPallete.blue,
child: Icon(Icons.photo_library),
),
),
],
),
),
);
}
}
The cleanest and easiest way to do this is through Provider. It is one of the state management solutions you can use to pass values around the app as well as rebuild only the widgets that changed. (Ex: When the value of the Text widget changes). Here is how you can use Provider in your scenario:
This is how your model should look like:
class ImageModel extends ChangeNotifier {
String _base64Image;
get base64Image => _base64Image;
set base64Image(String base64Image) {
_base64Image = base64Image;
notifyListeners();
}
}
Don't forget to add getters and setters so that you can use notifyListeners() if you have any ui that depends on it.
Here is how you can access the values of ImageModel in your UI:
final model=Provider.of<ImageModel>(context,listen:false);
String image=model.base64Image; //get data
model.base64Image=resultBase64; //set your image data after you used ImagePicker
Here is how you can display your data in a Text Widget (Ideally, you should use Selector instead of Consumer so that the widget only rebuilds if the value its listening to changes):
#override
Widget build(BuildContext context) {
//other widgets
Selector<ImageModel, String>(
selector: (_, model) => model.base64Image,
builder: (_, image, __) {
return Text(image);
},
);
}
)
}
You could achieve this easily. If you are using Blocs.

Flutter not updating DropdownButton placed within a dialog when item is selected

I have an alertDialog containing a DropdownButton widget. Whenever I click on an option within the dropDown I want it to display the selected value. I have listed the code below along with 2 screenshots.
I believe this may be an issue with how flutter builds widget because when I placed the DropdownButton widget outside of the dialog it worked, however placing it within an alertDialog causes it to fail. I also noticed that if I clicked on an option in the DropdownButton and then exited and clicked on the dialog again the selected item would change. However, I want the selected value to change without the user having to tap out of the dialog and then back in.
^
The above image is the dialog when a user first clicks on it. At first the only selected item is "I'm not able to help". This value should change whenever a user clicks on the DropdownMenu widget and selects a different option such as "other".
^
These are the various options a user can click on within the dropdown menu. When a user clicks on it, the menu should update accordingly.
CODE:
Please note that I have defined _chosenValue as a global variable outside of the build function.
void _showDecline() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Decline Appointment Request"),
content: Container(
height: 100,
width: 200,
child: Column(
children: <Widget>[
new Text("Please select an option for why you declined."),
new DropdownButton<String>(
value: _chosenValue,
underline: Container(),
items: <String>['I\'m not able to help', 'Unclear description', 'Not available at set date and time', 'Other'].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value, style: TextStyle(fontWeight: FontWeight.w500),),
);
}).toList(),
onChanged: (String value) {
setState(() {
_chosenValue = value;
});
},
)
],
),
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {},
},
),
],
);
},
);
}
setState will only update current StatefulWidget's Widget Build function.
You should use StatefulBuilder inside showDialog.
For your case just add StatefulBuilder as a parent of your DropDown widget, and use StateSetter when you want to update the StatefulBuilder's children.
It will only update the widget tree defined under StateFulBuilder builder function.
See the full code including stateFulBuilder at DartPad code StateFulBuilderDartPad.
For more information on StatefulBuilder head over to StateFulBuilder documentation page.
import 'dart:convert';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: HomePage());
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String _chosenValue;
void _showDecline() {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return AlertDialog(
title: new Text("Decline Appointment Request"),
content:
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
new Text("Please select an option for why you declined."),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: new DropdownButton<String>(
hint: Text('Select one option'),
value: _chosenValue,
underline: Container(),
items: <String>[
'I\'m not able to help',
'Unclear description',
'Not available at set date and time',
'Other'
].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(
value,
style: TextStyle(fontWeight: FontWeight.w500),
),
);
}).toList(),
onChanged: (String value) {
setState(() {
_chosenValue = value;
});
},
)),
]),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: FlatButton(child: Text('Click'), onPressed: _showDecline),
),
),
);
}
}
Just check out the below example you have to use the statefulBuilder to change the state.
import 'dart:convert';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: HomePage());
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String _chosenValue;
void _showDecline() {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState){
return AlertDialog(
title: new Text("Decline Appointment Request"),
content: Container(
height: 100,
width: 200,
child: Column(
children: <Widget>[
new Text("Please select an option for why you declined."),
new DropdownButton<String>(
hint: Text('Select one option'),
value: _chosenValue,
underline: Container(),
items: <String>[
'I\'m not able to help',
'Unclear description',
'Not available at set date and time',
'Other'
].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(
value,
style: TextStyle(fontWeight: FontWeight.w500),
),
);
}).toList(),
onChanged: (String value) {
setState(() {
_chosenValue = value;
});
},
)
],
),
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: FlatButton(child: Text('Click'), onPressed: _showDecline),
),
),
);
}
}
Just let me know if it works.
onTap: () {
///___________________________________________________________________
// Get.defaultDialog(
// title: " وضعیت دزدگیر",
// middleText: "پیام اعلام وضعیت دزدگیر ارسال گردد؟",
// titleStyle: TextStyle(
// color: mainColor2, fontWeight: FontWeight.bold, fontSize: 16),
// middleTextStyle:
// TextStyle(color: mainColor6.withOpacity(0.9), fontSize: 15),
// );
///----------------------------------------------------------------------
// showDialog(
// context: context,
// builder: (context) => AlertDialog(
// content: Column(
// children: <Widget>[
// TextField(
// decoration: InputDecoration(
// icon: Icon(Icons.account_circle),
// labelText: 'Username',
// ),
// ),
// TextField(
// obscureText: true,
// decoration: InputDecoration(
// icon: Icon(Icons.lock),
// labelText: 'Password',
// ),
// ),
// ],
// ),
// ),
// );
///___________________________________________________________________
List<DropdownMenuItem<String>> listDrop = [];
String selected=null;
void loadData() {
listDrop.add(new DropdownMenuItem(
child: new Text("پایدار"),
value:"555",
));
listDrop.add(
new DropdownMenuItem(
child: new Text("لحظه ای"),
value:"444",
),
);
}
loadData();
Alert(
context: context,
title: "تنظیمات خروجی شماره ۱",
// desc: ".",
// image: Image.asset(
// "assets/settings.png",
// scale: 5,
// ),
content: Directionality(
textDirection: TextDirection.rtl,
child: Center(
child: Column(
children: <Widget>[
SizedBox(height: 20.0),
TextField(
keyboardType: TextInputType.text,
controller: _codeShargController,
decoration: InputDecoration(
labelText: 'نام خروجی',
hintText: '${out1.read('codeShargController')}',
),
),
SizedBox(height: 25.0),
Center(
child: DropdownButton(
underline: Container(
height: 1.5,
color: Colors.black26,
),
hint: Text("وضعیت عملکرد"),
items: listDrop,
isExpanded: true,
value: selected,
style: TextStyle(color: Colors.black, fontSize: 16),
onChanged: (newValue) {
selected = newValue;
// setState(() {});
setState(() { selected = newValue; });
},
),
),
SizedBox(height: 25.0),
],
),
),
),
// content: Column(
// children: <Widget>[
//
// SizedBox(height: 10.0),
//
// TextField(
//
// decoration: InputDecoration(
//
// icon: Icon(Icons.account_circle),
// labelText: 'Username',
// ),
// ),
// SizedBox(height: 10.0),
//
// TextField(
// obscureText: true,
// decoration: InputDecoration(
// icon: Icon(Icons.lock),
// labelText: 'Password',
// ),
// ),
// ],
// ),
buttons: [
DialogButton(
onPressed: () {
out1.write(
"codeShargController", _codeShargController.text);
Navigator.pop(context);
},
child: Text(
"ثبت",
style: TextStyle(color: Colors.white, fontSize: 20),
),
)
]).show();
///___________________________________________________________________
Try This ......
implement the alert on separate dart file and call it. that worked for me.
Important - there was used following dropdown plugin because that ui better for me...
Link - dropdown_button2: ^1.2.2
on main page call to the alert as follows.
import 'package:crmapp/pages/payment_history/payment_history_search_dialog.dart';
import 'package:flutter/material.dart';
class PaymentHistoryScreen extends StatefulWidget {
#override
_PaymentHistoryScreenState createState() => _PaymentHistoryScreenState();
}
class _PaymentHistoryScreenState extends State<PaymentHistoryScreen> {
ScrollController scrollController = new ScrollController();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
// Setting up AppBar
appBar: AppBar(
title: Text('Payment History'),
),
// Body
body: Container(
// your code here - you can use onpressed method in the body also.here I used it for floating button
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return new PaymentHistorySearchDialog(); //call the alert dart
}
);
},
child: Container
(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(100)),
),
child: Icon(Icons.search_sharp, size: 32, color: Colors.white,)
)
),
);
}
then code the alert dart as follows.
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class PaymentHistorySearchDialog extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return PaymentHistorySearchDialogState();
}
}
class PaymentHistorySearchDialogState extends State<PaymentHistorySearchDialog> {
String? selectedValue;
List<String> items = [
'All',
'Completed',
'Pending',
'Rejected',
];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new AlertDialog(
titlePadding: EdgeInsets.only(top: 20, left: 15, right: 15, bottom: 5),
contentPadding: EdgeInsets.only(
top: 15,
left: 15,
right: 15,
bottom: 5
),
title: Text(
'Search'.toUpperCase(),
style: TextStyle(
color: Colors.black,
fontSize: 22,
fontWeight: FontWeight.w600,
fontFamily: "medium",
)
),
content: Container(
width: double.infinity,
height: 220,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
DropdownButtonHideUnderline(
child: DropdownButton2(
hint: Text(
'Select Status',
style: TextStyle(
fontSize: 14,
),
),
items: items
.map((item) =>
DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
))
.toList(),
value: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value as String;
//Navigator.of(context).pop();
});
print(value);
// selectedValue = value as String;
},
buttonHeight: 30,
buttonWidth: double.infinity,
itemHeight: 40,
buttonDecoration: BoxDecoration(
// borderRadius: BorderRadius.circular(14),
border: Border(
bottom: BorderSide(width: 1, color: Colors.black38),
),
),
buttonPadding: const EdgeInsets.only(bottom: 5, top: 5),
),
)
],
),
)
);
}
}

Custom widget doesn't show

I'am trying to create a custom widget CarteSim and I'am trying to call it everytime I click on Floating Action Button , but unfortunately it doesn't show when I call it inside the button , but it shows when I call it in the Scaffold , please who has ever experience that
here s my code
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.pink,
title: Text(" INWI "),
),
body: SingleChildScrollView(
child: Container(
child: Center(
child: Column(
children: <Widget>[
DropdownButton<String>(
items: _type.map((String dropDownStringItem) {
return DropdownMenuItem<String>(
value: dropDownStringItem,
child: Text(dropDownStringItem),
);
}).toList(),
onChanged: (String newValueSelected) {
setState(() {
this._currentItemSelected = newValueSelected;
});
},
value: _currentItemSelected,
),
Divider(
color: Colors.black,
),
new Row(
children: [
new Text(" type client supporte * carte avec 1 cin"),
new Checkbox(value: _isChecked, onChanged: (bool value){ onChanged(value);}),
]
),
Divider(
color: Colors.black,
),
new Column(
children: <Widget>[
CarteCin(),
CarteSim(),
]
)
],
),
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: (){
Column(
children: <Widget>[
CarteSim(),
]
);
},
child: Icon(Icons.add),
backgroundColor: Colors.pink,
),
);
}
]
}
you are misunderstanding the process. The onPressed key actually used for call actions on press the floatingActionButton. You can write functions there, like you can update your state or print anything on your console or call an API to get data. But you can't display any widget from there.
Now what you're trying to do, that can be resolved like this. Whenever you press that button you should update your state, i.e bool show. Then modify your Column children based on that show state.
Here is an example how tu update the UI in flutter
class FrontPage extends StatefulWidget {
#override
_FrontPageState createState() => _FrontPageState();
}
class _FrontPageState extends State<FrontPage> {
bool currentStateShowsThis;
initState() {
currentStateShowsThis = false;
super.initState();
}
Widget build(BuildContext context) {
final color = currentStateShowsThis ? Colors.blue : Colors.red;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.pink,
title: Text(" INWI "),
),
body: SingleChildScrollView(
child: Container(
child: Center(
child: Column(
children: <Widget>[
Container(width: 100, height: 100, color: color,) // alternatively you can change the widgets being displayed
],
),
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: (){
setState(() {
currentStateShowsThis = !currentStateShowsThis;
});
},
child: Icon(Icons.add),
backgroundColor: Colors.pink,
),
);
}
}
In your example "the button" will call back the
onPressed:
Which will only create the widgets in that method (Column ...) and not place them in a the layout.

Range Error keeps appearing when dismissible widget is run

Here is my code
import 'package:flutter/material.dart';
void main() => runApp(TodoList());
class TodoList extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _TodoListState();
}
}
var TaskTextField;
List taskTextList = [];
var TaskIsImportant = false;
class _TodoListState extends State<TodoList> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
title: "To-Do List V2",
home: Scaffold(
appBar: AppBar(
title: Text("To-Do List V2"),
),
body: Flex(
direction: Axis.vertical,
children: <Widget>[
Flexible(
fit: FlexFit.tight,
child: Column(children: <Widget>[
Container(
child: TextField(
decoration: InputDecoration(
hintText: "Enter title of task to be added"),
onChanged: (taskTextField) {
setState(() {
TaskTextField = taskTextField;
print(TaskTextField);
});
},
),
margin: EdgeInsets.all(16.0),
),
CheckboxListTile(
title: Text("Important"),
activeColor: Colors.blue,
value: TaskIsImportant,
onChanged: (val) {
setState(() {
TaskIsImportant = !TaskIsImportant;
print(TaskIsImportant);
});
},
),
Text(
"Tip: Tap on your task to remove it",
style: TextStyle(fontSize: 15.0),
),
Container(
margin: EdgeInsetsDirectional.fromSTEB(117, 10, 117, 5),
child: Row(
children: <Widget>[
RaisedButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
if (TaskIsImportant) {
taskTextList.add("❗️$TaskTextField");
//taskTitleList.add(TaskDetailField);
} else {
taskTextList.add(TaskTextField);
//taskTitleList.add(TaskDetailField);
}
});
},
),
RaisedButton(
child: Icon(Icons.done_all),
onPressed: () {
setState(() {
taskTextList = [];
});
},
),
],
),
),
Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
var title = taskTextList[index];
padding:
EdgeInsets.all(16.0);
return Dismissible(
key: Key(title[index]),
onDismissed: (direction) {
setState(() {
taskTextList.remove(title);
});
},
child: ListTile(
title: Text(
title,
style: TextStyle(fontSize: 20.0),
),
),
);
},
itemCount: taskTextList.length,
),
)
]),
),
],
)));
}
}
When I create four listTiles in the emulator, two errors are given as follows
flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following RangeError was thrown building:
flutter: RangeError (index): Invalid value: Not in range 0..3, inclusive: 4
I hope someone can tell me what is wrong with my code. I am running on flutter version 1.3.10. Thanks in advance.
Also to the person that needs more code, here is the full code of main.dart. Hope this will help you find my errors
You are using title[index] but saving the value in title as title = taskTextList[index] which is not an array. So, you are getting range error.