Flutter save the selected item from a dropdownmenu into a variable - flutter

i want to save my selected item form a dropdown menu into a variable, that i can use my selection in an other file to show it there in a dialog box.
Is there a simple way how i can save the selection in a var or someone knew a better way to get my selection into the other file?
import 'package:flutter/material.dart';
import 'package:dropdown_search/dropdown_search.dart';
var AderOutput;
class Ader extends StatefulWidget {
#override
_AderState createState() => _AderState();
}
class _AderState extends State<Ader> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(25),
child: DropdownSearch<String>(
validator: (v) => v == null ? "Erforderliches Feld" : null,
hint: "Wähle eine Ader",
mode: Mode.MENU,
showSelectedItem: true,
items: ["Ader 1", "Ader 2"],
label: "Wähle eine Ader",
showClearButton: true,
onChanged: print,
popupItemDisabled: (String s) => s.startsWith('I'),
selectedItem: null,
onBeforeChange: (a, b) {
if (b == null) {
AlertDialog alert = AlertDialog(
title: Text('Sind Sie sicher,'),
content: Text('dass Sie die Auswahl zurücksetzten möchten?'),
actions: [
TextButton(
child: Text("Ja"),
onPressed: () {
Navigator.of(context).pop(true);
},
),
TextButton(
child: Text("Nein"),
onPressed: () {
Navigator.of(context).pop(false);
},
),
],
);
return showDialog<bool>(
context: context,
builder: (BuildContext context) {
return alert;
});
}
return Future.value(true);
},
),
);
}
}

Related

select dropdown then show text box in flutter

I'm new utilising Flutter, and I'd like to know how to display a text box based on what the user chooses from a drop-down menu, for example, if the drop-down menu has options of burgers, vegetables, and fruits.
So, once the user chooses burger, the app will provide a text box with the message, "Do not eat that much." then the user will click okay and will close the text box
Thank you and I hope you can understand.
You can follow this snippet But must check DropdownButton StatefulWidget showDialog
void main(List<String> args) {
runApp(
MaterialApp(
home: DFR(),
),
);
}
class DFR extends StatefulWidget {
DFR({Key? key}) : super(key: key);
#override
State<DFR> createState() => _DFRState();
}
class _DFRState extends State<DFR> {
String? selected;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
DropdownButton<String?>(
items: ["burgers", "vegetables", "fruits"]
.map(
(e) => DropdownMenuItem<String>(
child: Text(e),
value: e,
),
)
.toList(),
onChanged: (v) async {
if (v == "burgers") {
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text("Do not eat that much."),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text("Ok"),
)
],
);
},
);
}
selected = v;
setState(() {});
},
),
Text(selected ?? ""),
],
),
);
}
}

BlocProvider.of() called with a context that does not contain a Bloc of type FicheMvtBloc

I'm developing a new Flutter Mobile app using the BLoC pattern . But I've got a problem and I don't find the solution yet.
The first one is my home page (with the MultiBlocProvider)
When I press on the FloatingActionButton.
It push a new screen to add a new "FicheMvt"
When I hit the add button.
It uses an onSave callback function to notify its parent of newly created "FicheMvt"
It gives me an error.
BlocProvider.of() called with a context that does not contain a Bloc
of type FicheMvtBloc.
No ancestor could be found starting from the context that was passed
to BlocProvider.of().
This can happen if the context you used comes from a widget above the
BlocProvider.
This is the home page (render 5 tab body)
class EtatCollecteScreen extends StatelessWidget {
final FicheMvtDAO ficheMvtDAO = FicheMvtDAO();
final FicheMvtReferenceDAO ficheMvtReferenceDAO = FicheMvtReferenceDAO();
#override
Widget build(BuildContext context) {
final FicheModel fiche = ModalRoute.of(context).settings.arguments;
return MultiBlocProvider(
providers: [
BlocProvider<TabEtatCollecteBloc>(
create: (context) => TabEtatCollecteBloc(),
),
BlocProvider<FicheMvtBloc>(
create: (context) => FicheMvtBloc(
ficheMvtDAO: ficheMvtDAO,
)..add(FicheMvtRequested(idFiche: fiche.id)),
),
BlocProvider<FicheMvtReferenceBloc>(
create: (context) => FicheMvtReferenceBloc(
ficheMvtReferenceDAO: ficheMvtReferenceDAO,
)..add(FicheMvtReferenceRequested(idFiche: fiche.id)),
),
],
child: EtatCollecteContent(
ficheModel: fiche,
),
);
}
}
class EtatCollecteContent extends StatelessWidget {
final FicheModel ficheModel;
const EtatCollecteContent({Key key, #required this.ficheModel});
#override
Widget build(BuildContext context) {
return BlocBuilder<TabEtatCollecteBloc, EtatCollecteTab>(
builder: (context, activeTab) {
return Scaffold(
appBar: AppBar(
title: Text("${ficheModel.id} - ${ficheModel.description}"),
actions: <Widget>[
RefreshMvtButton(
visible: activeTab == EtatCollecteTab.completed,
ficheModel: ficheModel,
),
SendMvtButton(
visible: activeTab == EtatCollecteTab.uncommitted,
ficheModel: ficheModel,
),
],
),
body: EtatCollecteBody(
activeTab: activeTab,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return FicheMvtAddScreen(onSaveCallback: (idFiche, indicateurModel, codeSite) {
BlocProvider.of<FicheMvtBloc>(context).add(
FicheMvtAdded(
idFiche: idFiche,
indicateurModel: indicateurModel,
codeSite: codeSite,
),
);
});
},
settings: RouteSettings(
arguments: ficheModel,
),
),
);
},
child: Icon(Icons.add),
tooltip: "Add",
),
bottomNavigationBar: TabEtatCollecteSelector(
activeTab: activeTab,
onTabSelected: (tab) => BlocProvider.of<TabEtatCollecteBloc>(context).add(TabEtatCollecteUpdated(tab)),
),
);
},
);
}
}
And this is the code of the form to add new "FicheMvt" which contains another block that manages the dynamic form (FicheMvtAddBloc).
typedef OnSaveCallback = Function(
int idFiche,
IndicateurModel indicateurModel,
String codeSite,
);
class FicheMvtAddScreen extends StatelessWidget {
final OnSaveCallback onSaveCallback;
const FicheMvtAddScreen({Key key, #required this.onSaveCallback}) : super(key: key);
#override
Widget build(BuildContext context) {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final FicheModel fiche = ModalRoute.of(context).settings.arguments;
final FicheMvtRepository ficheMvtRepository = FicheMvtRepository();
return Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text("${fiche.id} - ${fiche.description}"),
),
backgroundColor: Colors.white,
body: BlocProvider<FicheMvtAddBloc>(
create: (context) => FicheMvtAddBloc(
ficheMvtRepository: ficheMvtRepository,
idFiche: fiche.id,
)..add(NewFicheMvtFormLoaded(idFiche: fiche.id)),
child: FicheMvtAddBody(
ficheModel: fiche,
onSave: onSaveCallback,
),
),
);
}
}
This is the content of the form
class FicheMvtAddBody extends StatefulWidget {
final FicheModel ficheModel;
final OnSaveCallback onSave;
#override
_FicheMvtAddBodyState createState() => _FicheMvtAddBodyState();
FicheMvtAddBody({Key key, #required this.ficheModel, #required this.onSave});
}
class _FicheMvtAddBodyState extends State<FicheMvtAddBody> {
static final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
void _onIndicateurChanged(String indicateur) =>
BlocProvider.of<FicheMvtAddBloc>(context).add(NewFicheMvtIndicateurChanged(indicateur: indicateur));
void _onSiteChanged(String site) => BlocProvider.of<FicheMvtAddBloc>(context).add(NewFicheMvtSiteChanged(site: site));
final FicheModel fiche = ModalRoute.of(context).settings.arguments;
final txtIndicateur = Text("Indicateur");
final txtSite = Text("Site");
return BlocBuilder<FicheMvtAddBloc, FicheMvtAddState>(
builder: (context, state) {
return Form(
key: _formKey,
child: Center(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
SizedBox(height: 24.0),
txtIndicateur,
DropdownButtonFormField<String>(
isExpanded: true,
hint: Text("Choisissez l'indicateur"),
value: state.indicateur?.code ?? null,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
onChanged: (String newValue) {
_onIndicateurChanged(newValue);
},
items: state.indicateurs?.isNotEmpty == true
? state.indicateurs
.map((CodeDescriptionModel model) => DropdownMenuItem(value: model.code, child: Text(model.description)))
.toList()
: const [],
validator: (value) {
if (value == null || value.isEmpty) {
return 'Entrer l\'indicateur s\'il vous plait';
}
return null;
},
),
SizedBox(height: 24.0),
txtSite,
DropdownButtonFormField<String>(
isExpanded: true,
hint: Text("Choisissez le site"),
value: state.site?.code ?? null,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
onChanged: (String newValue) {
_onSiteChanged(newValue);
},
items: state.sites?.isNotEmpty == true
? state.sites.map((CodeDescriptionModel model) => DropdownMenuItem(value: model.code, child: Text(model.description))).toList()
: const [],
validator: (value) {
if (value == null || value.isEmpty) {
return 'Entrer le site s\'il vous plait';
}
return null;
},
),
Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
onPressed: () {
if (_formKey.currentState.validate()) {
widget.onSave(
fiche.id,
state.indicateur,
state.site?.code ?? null,
);
Navigator.pop(context);
}
},
padding: EdgeInsets.all(12),
color: Colors.blue,
child: Text('Create', style: TextStyle(color: Colors.white)),
),
)
],
),
),
);
},
);
}
}
Thanks for your help
You are using the wrong context in onSaveCallback. Here is a simplified hierarchy of your widgets:
- MaterialApp
- EtatCollecteScreen
- MultiBlocProvider
- FicheMvtAddScreen
So in your onSaveCallback you are accessing the context of FicheMvtAddScreen and it's obvious from the hierarchy above that BlocProvider couldn't find the requested Bloc. It's easy to fix this:
MaterialPageRoute(
builder: (pageContext) {
return FicheMvtAddScreen(onSaveCallback: (idFiche, indicateurModel, codeSite) {
BlocProvider.of<FicheMvtBloc>(context).add(
FicheMvtAdded(
idFiche: idFiche,
indicateurModel: indicateurModel,
codeSite: codeSite,
),
);
});
},
settings: RouteSettings(
arguments: ficheModel,
),
),
I've renamed context variable to pageContext in route builder function (so it wouldn't shadow required context). Now BlocProvider should able to find requested Bloc by accessing right context.
Another way to fix is to put MultiBlocProvider higher in widgets hierarchy.

How change the visual of List of Radio button?

I have a bet game it is a list of game and user need to choose between 3 choices (only one choice) (1,N,2) so i use radio in flutter for do it.
1 - Here is the result i have now :
2 - Here is the result i want to have :
I need have the same radio 1 N 2 without using any images, i think it is possible to do it using clipoval perhaps with rounded borders. Here the selected radio is in RED in background so when i click on another radio it needs change the color of radio (red background and white color font if selected and white background and red color font if not selected)
And now here is my entire code for the grid :
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'package:flutter_app/menu_member.dart';
import 'package:flutter_app/globals.dart' as globals;
import 'package:flutter_app/grille_lotosport.dart';
// Create
a Form widget.
class Affiche_grille extends StatefulWidget {
#override
String id;
Affiche_grille({Key key, #required this.id}) : super(key: key);
_Affiche_grille_State createState() {
return _Affiche_grille_State();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class _Affiche_grille_State extends State<Affiche_grille> {
#override
final _formKey = GlobalKey<FormState>();
List<String> radioValues = [];
Future<List<Match>> grid;
Future <List<Match>> Grille_display() async {
// SERVER LOGIN API URL
var url = 'https://www.easytrafic.fr/game_app/display_grid.php';
// Store all data with Param Name.
var data = {'id_grille': widget.id};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data),headers: {'content-type': 'application/json','accept': 'application/json','authorization': globals.token});
// Getting Server response into variable.
var jsondata = json.decode(response.body);
List<Match> Matchs = [];
for (var u in jsondata) {
Match match = Match(u["equipe1"],u["equipe2"],u["type_prono"]);
Matchs.add(match);
radioValues.add("N");
}
return Matchs;
}
void initState() {
grid = Grille_display();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('GRILLE LOTOSPORT'),
),
drawer: new DrawerOnly(),
body:
Container(
child:
FutureBuilder(
future: grid,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Center(
child: new CircularProgressIndicator(),);
default:
if (snapshot.hasError) {
return new Center(
child: new Text('Error: ${snapshot.error}'),);
}
else {
List<Match> values = snapshot.data;
return Form(
key: _formKey,
child: ListView(
children: <Widget>[
DataTable(
columnSpacing: 20,
columns: [
DataColumn(
label: Text("Libelle Match"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("1"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("N"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("2"),
numeric: false,
tooltip: "",
),
],
rows:
List.generate(values.length, (index) {
return DataRow(
cells: [
DataCell(
Text(values[index].equipe1.toString() +
" - " +
values[index].equipe2.toString()),
),
DataCell(
Radio(
value: "1",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
});
},
),
),
DataCell(
Radio(
value: "N",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
});
},
),
),
DataCell(
Radio(
value: "2",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
});
},
),
),
]
);
}).toList(),
),
Center(
child: RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(9, 9, 9, 9),
child: Text('VALIDER VOTRE GRILLE'),
onPressed: () {
Valide_grille();
},
),
),
],
)
);
};
};
},
),
),
),
);
}
Future Valide_grille() async{
// For CircularProgressIndicator.
bool visible = false ;
// Showing CircularProgressIndicator.
setState(() {
visible = true ;
});
// SERVER LOGIN API URL
var url = 'https://www.easytrafic.fr/game_app/valide_grid.php';
var concatenate='';
radioValues.forEach((item){
concatenate=concatenate+item;
});
var id_grille=widget.id;
// Store all data with Param Name.
var data = {'id_membre':globals.id_membre, 'id_grille':id_grille,'result':concatenate};
var grille_encode=jsonEncode(data);
// Starting Web API Call.
var response = await http.post(url, body: grille_encode,headers: {'content-type': 'application/json','accept': 'application/json','authorization': globals.token});
print(response.body);
// Getting Server response into variable.
Map <String,dynamic> map2 = json.decode(response.body);
// If the Response Message is Matched.
if(map2["status"] == 1)
{
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(map2["message"]),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Affiche_Liste_grille()),
);
},
),
],
);
},
);
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
}else{
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
// Showing Alert Dialog with Response JSON Message.
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(map2["message"]),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
class Match {
final String equipe1;
final String equipe2;
final String typeprono;
const Match(this.equipe1, this.equipe2, this.typeprono);
}
I have found the solution, i Don't understand why it works but it works now with allmy corrections. I Don't understand why when i put a setstate all elements are updated correctly, i put no code in the setstate but all is now updated, it is very strange

Minlines or similar in TextField Flutter

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Todo {
String title;
String description;
Todo(this.title, this.description);
}
class TextEditingControllerWorkaroud extends TextEditingController {
TextEditingControllerWorkaroud({String text}) : super(text: text);
void setTextAndPosition(String newText, int caretPosition) {
int offset = caretPosition != null ? caretPosition : newText.length;
value = value.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: offset),
composing: TextRange.empty);
}
}
void main() {
runApp(MaterialApp(
title: 'Passing Data',
home: TodosScreen(
todos: List.generate(
20,
(i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
),
),
));
}
class TodosScreen extends StatelessWidget {
final List<Todo> todos;
TodosScreen({Key key, #required this.todos}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todos'),
),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index].title),
onTap: () async {
Map results = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen1(todo: todos[index]),
),
);
if (results["new"]!=results["old"] || results["newTitle"]!=results["oldTitle"]){
todos[index].description = results["new"];
todos[index].title = results["oldTitle"];
final snackBar = SnackBar(duration: Duration(milliseconds: 2000),
content: Text('Todo Saved Succesfully'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
print("go back to old");
todos[index].description = results["old"];
todos[index].title = results["oldTitle"];
},
),
);
// Find the Scaffold in the Widget tree and use it to show a SnackBar!
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(snackBar);
}
},
);
},
),
);
}
}
class DetailScreen1 extends StatefulWidget {
final Todo todo;
DetailScreen1({Key key, #required this.todo}) : super(key: key);
#override
DetailScreen1State createState() => DetailScreen1State();
}
class DetailScreen1State extends State<DetailScreen1> {
var descriptionTextContent = "";
var titleTextContent = "";
#override
void initState() {
super.initState();
print("intialized");
descriptionTextContent = widget.todo.description;
titleTextContent = widget.todo.title;
}
#override
Widget build(BuildContext context) {
TextEditingControllerWorkaroud descriptionEditWidgetController =
TextEditingControllerWorkaroud(text: descriptionTextContent);
TextEditingControllerWorkaroud titleEditWidgetController =
TextEditingControllerWorkaroud(text: titleTextContent);
TextField descriptionEditWidget = new TextField(
maxLines: 4,
keyboardType: TextInputType.multiline,
controller: descriptionEditWidgetController,
onChanged: (value) {
handleCurrentText(value, descriptionEditWidgetController);
},
);
TextField titleEditWidget = new TextField(
maxLines: 1,
keyboardType: TextInputType.text,
controller: titleEditWidgetController,
onChanged: (value) {
handleCurrentTitle(value, titleEditWidgetController);
},
);
descriptionEditWidgetController.setTextAndPosition(
descriptionTextContent, descriptionTextContent.length);
titleEditWidgetController.setTextAndPosition(
titleTextContent, titleTextContent.length);
return WillPopScope(
child: Scaffold(
appBar: AppBar(
title: Text(widget.todo.title),
leading: new IconButton(
icon: new Icon(Icons.arrow_back),
onPressed: () {
SystemChannels.textInput.invokeMethod('TextInput.hide');
Navigator.pop(context, {
'new': descriptionTextContent,
"old": widget.todo.description,
"newTitle": titleTextContent,
"oldTitle": widget.todo.title,
},
);
},
),
),
body: Padding(
padding: EdgeInsets.all(16.0), child: Column(children: <Widget>[titleEditWidget, descriptionEditWidget],)),
),
onWillPop: () {
Navigator.pop(context, {
'new': descriptionTextContent,
"old": widget.todo.description,
"newTitle": titleTextContent,
"oldTitle": widget.todo.title,
},
);
},
);
}
handleCurrentText(String value,
TextEditingControllerWorkaroud descriptionEditWidgetController) {
setState(() {
descriptionTextContent = value;
print("value is " + value);
});
}
void handleCurrentTitle(String value, TextEditingControllerWorkaroud titleEditWidgetController) {
setState(() {
titleTextContent = value;
});
}
}
The code above is properly functioning code that can be run directly. I have the problem that is TextField has property maxlines. If its null then it auto adjust as the text size grows/shrinks. And if we set it to constant as soon as we increase textContent it acts like scrollable in the widget. But what I want is something called "minLines" that is We start with default no of lines(like if we set max lines to constant) and then we can adjust the size of TextField if text grows(like if we set max lines to null). Also when the text content is beyond the range under the screen it becomes scrollable.
I would have handled that easily if I would be allowed to change maxLines attribute at runtime. I would have simply set a listener on textChange and managed the limits. But its final so I can edit that as well. What can I do?
In recent versions of Flutter, there is actually minLines parameter of TextField and it acts as you described.
First of all the very first mistake that I think was that there was no use of changing state continuously as the user enters the text.My work around was to change state only when I want maxlines to edit
So I set maxlines to a variable defined in the class and that variable I modify as soon as I wanted it (when text exceeds the no of characters)
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Todo {
String title;
String description;
Todo(this.title, this.description);
}
class TextEditingControllerWorkaroud extends TextEditingController {
TextEditingControllerWorkaroud({String text}) : super(text: text);
void setTextAndPosition(String newText, int caretPosition) {
int offset = caretPosition != null ? caretPosition : newText.length;
value = value.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: offset),
composing: TextRange.empty);
}
}
void main() {
runApp(MaterialApp(
title: 'Passing Data',
home: TodoScreen(
todos: List.generate(
5,
(i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
),
),
));
}
class TodoScreenState extends State<TodoScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todos'),
),
floatingActionButton: new FloatingActionButton(
onPressed: () async {
setState(() {
print("pressed");
Todo newTodo = Todo("todo", "");
widget.todos.insert(widget.todos.length, newTodo);
});
int index = widget.todos.length - 1;
Map results = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen1(todo: widget.todos[index]),
),
);
if (results["new"] != results["old"] ||
results["newTitle"] != results["oldTitle"]) {
widget.todos[index].description = results["new"];
widget.todos[index].title = results["newTitle"];
final snackBar = SnackBar(
duration: Duration(milliseconds: 2000),
content: Text('Todo Saved Succesfully'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
setState(() {
widget.todos[index].description = results["old"];
widget.todos[index].title = results["oldTitle"];
});
},
),
);
// Find the Scaffold in the Widget tree and use it to show a SnackBar!
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(snackBar);
}
},
child: Icon(Icons.add),
),
body: ListView.builder(
itemCount: widget.todos.length,
itemBuilder: (context, index) {
return Dismissible(
background: Container(color: Colors.green[700]),
key: Key(widget.todos[index].title),
onDismissed: (direction) {
print(direction);
Todo currentTodo = widget.todos[index];
setState(() {
widget.todos.removeAt(index);
});
final snackBar = SnackBar(
duration: Duration(milliseconds: 2000),
content: Text('Todo Deleted Succesfully'),
action: SnackBarAction(
label: 'Undo',
onPressed: () async {
setState(() {
widget.todos.insert(index, currentTodo);
});
},
),
);
// Find the Scaffold in the Widget tree and use it to show a SnackBar!
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(snackBar);
},
child: ListTile(
title: Text(widget.todos[index].title),
onTap: () async {
Map results = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DetailScreen1(todo: widget.todos[index]),
),
);
if (results["new"] != results["old"] ||
results["newTitle"] != results["oldTitle"]) {
widget.todos[index].description = results["new"];
widget.todos[index].title = results["newTitle"];
final snackBar = SnackBar(
duration: Duration(milliseconds: 2000),
content: Text('Todo Saved Succesfully'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
setState(() {
widget.todos[index].description = results["old"];
widget.todos[index].title = results["oldTitle"];
});
},
),
);
// Find the Scaffold in the Widget tree and use it to show a SnackBar!
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(snackBar);
}
},
),
);
},
),
);
}
}
class TodoScreen extends StatefulWidget {
final List<Todo> todos;
TodoScreen({Key key, #required this.todos}) : super(key: key);
#override
TodoScreenState createState() => TodoScreenState();
}
class DetailScreen1 extends StatefulWidget {
final Todo todo;
DetailScreen1({Key key, #required this.todo}) : super(key: key);
#override
DetailScreen1State createState() => DetailScreen1State();
}
class DetailScreen1State extends State<DetailScreen1> {
var descriptionTextContent = "";
var titleTextContent = "";
var size = 3;
var currentSize="fixed";
#override
void initState() {
super.initState();
print("intialized");
descriptionTextContent = widget.todo.description;
titleTextContent = widget.todo.title;
if (descriptionTextContent.length>=100){
size=null;
currentSize="variable";
}
}
#override
Widget build(BuildContext context) {
TextEditingControllerWorkaroud descriptionEditWidgetController =
TextEditingControllerWorkaroud(text: descriptionTextContent);
TextEditingControllerWorkaroud titleEditWidgetController =
TextEditingControllerWorkaroud(text: titleTextContent);
TextField descriptionEditWidget = new TextField(
decoration: new InputDecoration(hintText: 'Description'),
maxLines: size,
keyboardType: TextInputType.multiline,
controller: descriptionEditWidgetController,
onChanged: (value) {
handleCurrentText(value, descriptionEditWidgetController);
},
);
TextField titleEditWidget = new TextField(
decoration: new InputDecoration(hintText: 'Title'),
maxLines: 1,
keyboardType: TextInputType.text,
controller: titleEditWidgetController,
onChanged: (value) {
handleCurrentTitle(value, titleEditWidgetController);
},
);
descriptionEditWidgetController.setTextAndPosition(
descriptionTextContent, descriptionTextContent.length);
titleEditWidgetController.setTextAndPosition(
titleTextContent, titleTextContent.length);
return WillPopScope(
child: Scaffold(
appBar: AppBar(
title: Text(widget.todo.title),
leading: new IconButton(
icon: new Icon(Icons.arrow_back),
onPressed: () {
SystemChannels.textInput.invokeMethod('TextInput.hide');
Navigator.pop(
context,
{
'new': descriptionTextContent,
"old": widget.todo.description,
"newTitle": titleTextContent,
"oldTitle": widget.todo.title,
},
);
},
),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: <Widget>[titleEditWidget, descriptionEditWidget],
)),
),
onWillPop: () {
Navigator.pop(
context,
{
'new': descriptionTextContent,
"old": widget.todo.description,
"newTitle": titleTextContent,
"oldTitle": widget.todo.title,
},
);
},
);
}
handleCurrentText(String value,
TextEditingControllerWorkaroud descriptionEditWidgetController) {
descriptionTextContent = value;
if (descriptionTextContent.length>100 && currentSize=="fixed"){
setState(() {
print("called");
size = null;
currentSize="variable";
});
}
else if (descriptionTextContent.length<=100&&currentSize=="variable")
{
setState(() {
print("called");
size = 3;
currentSize="fixed";
});
}
}
void handleCurrentTitle(
String value, TextEditingControllerWorkaroud titleEditWidgetController) {
titleTextContent = value;
}
}
The function to note is handleTextChange of descripionTextField

Dynamic list of check box tile in alert dialog not working

There is no clear answer on how to implement a checkbox tile in a dialog and set the state to work.
A print statement is working in setting the state of the checkbox is not changing, but other statements are working. Where can I find the answer?
I am using a dialog with multiple check boxes for multi select. Is there another of implementing multiselect in Flutter?
child: TextFormField(
decoration: InputDecoration(
labelText: 'Team Leader',
labelStyle: TextStyle(color: Colors.black)),
controller: teamLeaderController,
enabled: false,
style: TextStyle(color: Colors.black),
),
onTap: () {
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return CheckBoxDialog(context, teamLeader,
"Choose Team Leader", teamLeaderController, onSubmit);
});
}),
class CheckBoxState extends State<CheckBoxDialog> {
BuildContext context;
List<String> places;
String title;
TextEditingController con;
bool state;
CheckBoxState(this.context, this.places, this.title, this.con);
#override
void initState() {
super.initState();
state = false;
}
#override
Widget build(BuildContext context) {
return new AlertDialog(
title: new Text(title),
content:
Column(children: getMultiSelectOption(context, places, con, state)),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
child: Text('Ok'),
onPressed: () {
widget.onSubmit("");
Navigator.of(context).pop();
})
],
);
}
List<Widget> getMultiSelectOption(BuildContext context, List<String> places,
TextEditingController con, bool state) {
List<Widget> options = [];
List<String> selectedList = [];
for (int i = 0; i < places.length; i++) {
options.add(CheckboxListTile(
title: Text(places[i]),
value: selectedList.contains(places[i]),
onChanged: (bool value) {
print("on change: $value title: ${places[i]}");
setState(() {
if (value) {
selectedList.add(places[i]);
} else {
selectedList.remove(places[i]);
}
print("contains: ${selectedList.contains(places[i])}");
print("status: $value");
});
}));
}
return options;
}
}
Suppose you have a Dialog with some Widgets such as RadioListTile, DropdowButton… or anything that might need to be updated WHILE the dialog remains visible, how to do it?
Look at this example here.
https://www.didierboelens.com/2018/05/hint-5-how-to-refresh-the-content-of-a-dialog-via-setstate/
Suppose you have a Dialog with some Widgets such as RadioListTile, DropdowButton… or anything that might need to be updated WHILE the dialog remains visible, how to do it?
Difficulty: Beginner
Background
Lately I had to display a Dialog to let the user select an item from a list and I wanted to display a list of RadioListTile.
I had no problem to show the Dialog and display the list, via the following source code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class Sample extends StatefulWidget {
#override
_SampleState createState() => new _SampleState();
}
class _SampleState extends State<Sample> {
List<String> countries = <String>['Belgium','France','Italy','Germany','Spain','Portugal'];
int _selectedCountryIndex = 0;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){_showDialog();});
}
_buildList(){
if (countries.length == 0){
return new Container();
}
return new Column(
children: new List<RadioListTile<int>>.generate(
countries.length,
(int index){
return new RadioListTile<int>(
value: index,
groupValue: _selectedCountryIndex,
title: new Text(countries[index]),
onChanged: (int value) {
setState((){
_selectedCountryIndex = value;
});
},
);
}
)
);
}
_showDialog() async{
await showDialog<String>(
context: context,
builder: (BuildContext context){
return new CupertinoAlertDialog(
title: new Text('Please select'),
actions: <Widget>[
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Cancel');},
child: new Text('Cancel'),
),
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Accept');},
child: new Text('Accept'),
),
],
content: new SingleChildScrollView(
child: new Material(
child: _buildList(),
),
),
);
},
barrierDismissible: false,
);
}
#override
Widget build(BuildContext context) {
return new Container();
}
}
I was surprised to see that despite the setState in lines #34-36, the selected RadioListTile was not refreshed when the user tapped one of the items.
Explanation
After some investigation, I realized that the setState() refers to the stateful widget in which the setState is invoked. In this example, any call to the setState() rebuilds the view of the Sample Widget, and not the one of the content of the dialog. Therefore, how to do?
Solution
A very simple solution is to create another stateful widget that renders the content of the dialog. Then, any invocation of the setState will rebuild the content of the dialog.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class Sample extends StatefulWidget {
#override
_SampleState createState() => new _SampleState();
}
class _SampleState extends State<Sample> {
List<String> countries = <String>['Belgium','France','Italy','Germany','Spain','Portugal'];
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){_showDialog();});
}
_showDialog() async{
await showDialog<String>(
context: context,
builder: (BuildContext context){
return new CupertinoAlertDialog(
title: new Text('Please select'),
actions: <Widget>[
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Cancel');},
child: new Text('Cancel'),
),
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Accept');},
child: new Text('Accept'),
),
],
content: new SingleChildScrollView(
child: new Material(
child: new MyDialogContent(countries: countries),
),
),
);
},
barrierDismissible: false,
);
}
#override
Widget build(BuildContext context) {
return new Container();
}
}
class MyDialogContent extends StatefulWidget {
MyDialogContent({
Key key,
this.countries,
}): super(key: key);
final List<String> countries;
#override
_MyDialogContentState createState() => new _MyDialogContentState();
}
class _MyDialogContentState extends State<MyDialogContent> {
int _selectedIndex = 0;
#override
void initState(){
super.initState();
}
_getContent(){
if (widget.countries.length == 0){
return new Container();
}
return new Column(
children: new List<RadioListTile<int>>.generate(
widget.countries.length,
(int index){
return new RadioListTile<int>(
value: index,
groupValue: _selectedIndex,
title: new Text(widget.countries[index]),
onChanged: (int value) {
setState((){
_selectedIndex = value;
});
},
);
}
)
);
}
#override
Widget build(BuildContext context) {
return _getContent();
}
}