Related
I want to call 2 API endpoints at the same time, and I try to use MultiBlocProvider but it does not work, and here is some code.
https://pub.dev/documentation/flutter_bloc/latest/flutter_bloc/MultiBlocProvider-class.html
how could I use it in the correct ways, or I should another ways.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("${allTranslations.text('filterHistory')}"),
),
body: MultiBlocProvider(
providers: [
BlocProvider<HistoryBloc>(
create: (BuildContext context) => historyBloc,
child: BlocListener<HistoryBloc, HistoryState?>(
listener: (context, state) {
print('State loading ${state.runtimeType}');
if(state is HistoryStateSuccess) {
nextPageData(state.historyResModel);
}
if (state is HistoryStateError) {
appShowSnackBar(context, state.runtimeType.toString());
}
},
),
),
BlocProvider<PackingBloc>(
create: (BuildContext context) => packingBloc,
child: BlocListener<PackingBloc, PackingState?>(
listener: (context, state) {
print('State loading ${state.runtimeType}');
if(state is GetPackingStateSeccess) {
getParkingList(state.packingList);
}
if (state is GetPackingStateError) {
appShowSnackBar(context, state.runtimeType.toString());
}
},
),
),
],
child: _bodyBuilder()
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
heroTag: 'selectDateRange',
elevation: 10.0,
child: const Icon(Icons.calendar_today),
onPressed: () => {_selectDateRange()},
),
const SizedBox(height: 6),
FloatingActionButton(
heroTag: 'onMapTypeButtonPressed',
elevation: 10.0,
child: const Icon(Icons.map),
onPressed: _onMapTypeButtonPressed,
),
const SizedBox(height: 6),
FloatingActionButton(
heroTag: 'vehicleLocation',
child: const Icon(Icons.car_repair_sharp),
onPressed: () => _controller.animateCamera(
CameraUpdate.newCameraPosition(VehicleLocationPint())),
),
const SizedBox(height: 6),
FloatingActionButton(
heroTag: 'inittialCameraPosition',
elevation: 10.0,
child: const Icon(Icons.gps_fixed),
onPressed: () => _controller.animateCamera(
CameraUpdate.newCameraPosition(_inittialCameraPosition())),
),
],
),
);
};
Thanks you for your answer.
I'm working on a list in flutter that can be reordered.
However, I am getting an error.
I thought showDialog and builder were the cause of the error, so I turned off showDialog and tried again, but the error still occurred.
How to solve this problem or
How do I create a List using ReorderableListView.builder and showDialog?
I do not understand English, so some words and sentences may be wrong.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() {
// 最初に表示するWidget
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
// 右上に表示される"debug"ラベルを消す
debugShowCheckedModeBanner: false,
// アプリ名
title: 'My Todo App',
theme: ThemeData(
// テーマカラー
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
// リスト一覧画面を表示
home: TodoListPage(),
);
}
}
// リスト一覧画面用Widget
class TodoListPage extends StatefulWidget {
#override
_TodoListPageState createState() => _TodoListPageState();
}
class _TodoListPageState extends State<TodoListPage> {
// Todoリストのデータ
List<String> todoList = [];
void reorderData(int oldindex, int newindex) {
setState(() {
if (newindex > oldindex) {
newindex -= 1;
}
final items = todoList.removeAt(oldindex);
todoList.insert(newindex, items);
});
}
void sorting() {
setState(() {
todoList.sort();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
// AppBarを表示し、タイトルも設定
appBar: AppBar(
title: Text('List list'),
),
// データを元にListViewを作成
body: ReorderableListView.builder(
itemCount: todoList.length,
itemBuilder: (context, index) {
return Column(
children: <Widget>[
for (final items in todoList)
GestureDetector(
onTap: () async {
// ダイアログを表示------------------------------------
showDialog(
context: context,
builder: (BuildContext context) {
return Column(
children: <Widget>[
AlertDialog(
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Column(
children: <Widget>[
TextButton.icon(
icon: const Icon(
Icons.add,
color: Colors.black,
),
label: const Text('Edit'),
onPressed: () {
sorting();
},
onLongPress: () async {
var morenewText =
await Navigator.of(context)
.push(
MaterialPageRoute(
builder: (context) =>
TodoAddPage(
todoList[index]),
),
);
setState(() {
todoList[index] = morenewText;
});
},
),
ElevatedButton(
child: const Text('Delete'),
onPressed: () {
setState(() {});
todoList.removeAt(index);
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
primary: Colors.blue,
),
),
ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
],
),
),
),
],
);
},
);
},
key: ValueKey(items),
child: ListTile(
title: Text(todoList[index]),
),
),
],
);
},
onReorder: reorderData,
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
// "push"で新規画面に遷移
// リスト追加画面から渡される値を受け取る
var newListText = await Navigator.of(context).push(
MaterialPageRoute(builder: (context) {
// 遷移先の画面としてリスト追加画面を指定
return TodoAddPage(null);
}),
);
if (newListText != null) {
// キャンセルした場合は newListText が null となるので注意
setState(() {
// リスト追加
todoList.add(newListText);
});
}
},
child: Icon(Icons.add),
),
);
}
}
class TodoAddPage extends StatefulWidget {
final oldnama;
TodoAddPage(this.oldnama);
#override
_TodoAddPageState createState() => _TodoAddPageState();
}
class _TodoAddPageState extends State<TodoAddPage> {
var newname = '';
#override
Widget build(BuildContext context) {
if (widget.oldnama != null) {
newname = widget.oldnama;
}
return Scaffold(
appBar: AppBar(
title: Text('Add list'),
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// 入力されたテキストを表示
const SizedBox(height: 8),
// テキスト入力
TextFormField(
//テキスト入力の初期値を決める
initialValue: newname,
// 入力されたテキストの値を受け取る(valueが入力されたテキスト)
onChanged: (String value) {
// データが変更したことを知らせる(画面を更新する)
// データを変更
newname = value;
},
),
const SizedBox(height: 8),
Container(
// 横幅いっぱいに広げる
width: double.infinity,
// リスト追加ボタン
child: ElevatedButton(
onPressed: () {
// "pop"で前の画面に戻る
// "pop"の引数から前の画面にデータを渡す
Navigator.of(context).pop(newname);
},
child: Text('Add list', style: TextStyle(color: Colors.white)),
),
),
const SizedBox(height: 8),
Container(
// 横幅いっぱいに広げる
width: double.infinity,
// キャンセルボタン
child: TextButton(
// ボタンをクリックした時の処理
onPressed: () {
// "pop"で前の画面に戻る
Navigator.of(context).pop();
},
child: Text('Cancel'),
),
),
],
),
),
);
}
}
You're using itemBuilder: wrong. Update it using this...
itemBuilder: (context, index) {
return ListTile(
key: ValueKey(todoList[index]),
onTap: () {
// ダイアログを表示------------------------------------
showDialog(
context: context,
builder: (BuildContext context) {
return Column(
children: <Widget>[
AlertDialog(
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Column(
children: <Widget>[
TextButton.icon(
icon: const Icon(
Icons.add,
color: Colors.black,
),
label: const Text('Edit'),
onPressed: () {
sorting();
},
onLongPress: () async {
var morenewText =
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
TodoAddPage(todoList[index]),
),
);
setState(() {
todoList[index] = morenewText;
});
},
),
ElevatedButton(
child: const Text('Delete'),
onPressed: () {
setState(() {});
todoList.removeAt(index);
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
primary: Colors.blue,
),
),
ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
],
),
),
),
],
);
},
);
},
title: Text(todoList[index]),
);
},
how do I call the alert dialog from another dart file, when the user clicks the button in addstudents.dart, I want to make the alert dialog in another file just in case it can be reused? In my addstudents.dart i have this Container, please see the code below, thanks.
addstudents.dart
Container(
padding: EdgeInsets.only(top: 10),
width: (globals.screenWidth * 0.48),
height: (globals.screenHeight * 0.10),
decoration:
BoxDecoration(borderRadius: BorderRadius.circular(10)),
child: RaisedButton(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(10.0),
),
onPressed: () {
// calling another file dart
},
color: Colors.green,
child: RichText(
text: TextSpan(
text: "Confirmed!",
style: TextStyle(
fontWeight: FontWeight.bold,
fontFamily: "Nunito",
color: Colors.white,
fontSize: globals.fontsize_19)),
),
),
),
this is my alertdialog.dart
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter'),
),
body: MyLayout()),
);
}
}
class MyLayout extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
child: Text('Show alert'),
onPressed: () {
showAlertDialog(context);
},
),
);
}
}
showAlertDialog(BuildContext context) {
Widget okButton = FlatButton(
child: Text("OK"),
onPressed: () {},
);
AlertDialog alert = AlertDialog(
title: Text("My title"),
content: Text("This is my message."),
actions: [
okButton,
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
Dunno if this is the best practice but here's what I'm doing
class Alerts {
static showAlertDialog(BuildContext context) {
Widget okButton = FlatButton(
child: Text("OK"),
onPressed: () {},
);
AlertDialog alert = AlertDialog(
title: Text("My title"),
content: Text("This is my message."),
actions: [
okButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
}
I call them using Alerts.showAlertDialog(context);
I am new to flutter.
In my real problem, my client is in places where it is very frequent that the internet is very slow, so sometimes an attempt is made to make a web request and this may take time, so the user leaves the screen before the web request is completed. Sometimes my app after completing a web request generates a dialog. So here is where my problem lies, the user is trying to make a web request and while it is done, they leave the screen and then the dialog is generated.
I am trying to simulate this problem with a delay that later generates the dialog.
I am not thinking of any strategy to end the web request, what I want is to find a way that once I leave the screen, causes the dialog not to be generated something like a dispose
I made an example where I have 2 screens. On the second screen a dialog is generated with a delay of 5 seconds when the button is clicked. If I navigate to another screen before the dialog is opened I get an error. I assume this occurs because the view was destroyed and therefore the dialog cannot be opened.
What can I do to avoid the error when the dialog is generated after being in another view? if I am in another view I DO NOT WANT the dialog to be generated.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("main");
return MaterialApp(title: 'Provider Example', initialRoute: '/', routes: {
'/': (context) => Home(),
'home': (context) => Home(),
'dialogpage': (context) => Dialogpage(),
});
}
}
class Home extends StatelessWidget {
Home() {
print("home");
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text('home'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.add_alert),
tooltip: 'Show Snackbar',
onPressed: () {
Navigator.pushNamed(context, "dialogpage");
},
),
],
),
body: const Center(
child: Text(
'home',
style: TextStyle(fontSize: 24),
),
),
);
}
}
class Dialogpage extends StatelessWidget {
Dialogpage() {
print("dialogpage");
}
dialog(BuildContext context) {
Future.delayed(const Duration(seconds: 5), () {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
title: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(19.0),
topRight: Radius.circular(19.0),
),
),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 5),
child: Text(
'Error',
style: TextStyle(color: Colors.white),
),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Icon(
Icons.error,
size: 50,
),
),
Text("dialog"),
],
),
titlePadding: EdgeInsets.all(0),
actions: <Widget>[
FlatButton(
child: Text('Aceptar'),
onPressed: () {
return Navigator.of(context).pop();
}),
],
);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dialog'),
),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
}),
),
);
}
}
use Globalkey in scaffold in then check the context in dialog method is it != null
then run dialog otherwise don't...
GlobalKey _scafolldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scafolldKey,
appBar: AppBar(
title: const Text('dialog'),),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
}),
),
);
}
}
dialog(BuildContext context) {
Future.delayed(const Duration(seconds: 2), () {
if(_scafolldKey.currentContext !=null){
showDialog();
}
});
}
Instead of Future.delayed, you should use Timer, which can be cancelled in onDispose method.
Working solution:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("main");
return MaterialApp(
title: 'Provider Example',
initialRoute: '/',
routes: {
'/': (context) => Home(),
'home': (context) => Home(),
'dialogpage': (context) => Dialogpage(),
},
);
}
}
class Home extends StatelessWidget {
Home() {
print("home");
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('home'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.add_alert),
tooltip: 'Show Snackbar',
onPressed: () {
Navigator.pushNamed(context, "dialogpage");
},
),
],
),
body: const Center(
child: Text(
'home',
style: TextStyle(fontSize: 24),
),
),
);
}
}
class Dialogpage extends StatefulWidget {
#override
_DialogpageState createState() => _DialogpageState();
}
class _DialogpageState extends State<Dialogpage> {
Timer _timer;
#override
void dispose() {
_timer?.cancel();
super.dispose();
}
dialog(BuildContext context) {
_timer = Timer(
const Duration(seconds: 3),
() {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
title: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(19.0),
topRight: Radius.circular(19.0),
),
),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 5),
child: Text(
'Error',
style: TextStyle(color: Colors.white),
),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Icon(
Icons.error,
size: 50,
),
),
Text("dialog"),
],
),
titlePadding: EdgeInsets.all(0),
actions: <Widget>[
FlatButton(
child: Text('Aceptar'),
onPressed: () {
return Navigator.of(context).pop();
},
),
],
);
},
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dialog'),
),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
},
),
),
);
}
}
Try this code
class Dialogpage extends StatelessWidget {
...
Timer t;
dialog(BuildContext context) {
t = Timer(Duration(seconds: 5), () {
showDialog(...);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('dialog'),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
t?.cancel();
Navigator.of(context).pop();
},
),
),
body: Center(
child: RaisedButton(
child: Text("show dialog"),
onPressed: () {
dialog(context);
}),
),
);
}
}
Hope it helps.
I am getting the following error in my code.
The getter 'todoText' isn't defined for the class 'String'.
Try importing the library that defines 'todoText', correcting the name to the name of an existing getter, or defining a getter or field named 'todoText'.dart(undefined_getter)
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
void main() => runApp(new ToDoApp());
class ToDoApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'To Do Application',
theme: new ThemeData(
primarySwatch: Colors.teal,
),
home: new ToDoList(),
);
}
}
class ListItem{
bool _todoCheck;
ListItem(this._todoCheck);
}
class _strikeThrough extends StatelessWidget{
final String todoText;
final bool todoCheck;
_strikeThrough(this.todoText, this.todoCheck) : super();
Widget _widget(){
if(todoCheck){
return Text(
todoText,
style: TextStyle(
decoration: TextDecoration.lineThrough,
fontStyle: FontStyle.italic,
fontSize: 22.0,
color: Colors.red[200],
),
);
}
else{
return Text(
todoText,
style: TextStyle(
fontSize: 22.0
),
);
}
}
#override
Widget build(BuildContext context){
return _widget();
}
}
class ToDoList extends StatefulWidget {
#override
createState() => new _TodoListState();
}
class _TodoListState extends State<ToDoList> {
var textController = TextEditingController();
var popUpTextController = TextEditingController();
List<String> _todoItems = [];
List<ListItem> WidgetList =[];
void _addToDoItem(String task){
if(task.length > 0){
setState(()=> _todoItems.add(task));
}
}
void _removeToDoItem(int index){
setState(() => _todoItems.removeAt(index));
}
void _promptRemoveToDoItem(int index){
showDialog(context: context,
builder: (BuildContext context){
return new AlertDialog(
title: new Text('Mark "${_todoItems[index]}" as Completed?'+' This will remove this Item'),
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
onPressed: () => Navigator.of(context).pop()
),
new FlatButton(
child: new Text('MARK AS DONE'),
onPressed: () {
_removeToDoItem(index);
Navigator.of(context).pop();
Fluttertoast.showToast(
msg: 'ToDo Task Deleted!'
);
}
),
]
);
}
);
}
Widget _buildToDoList(BuildContext context){
return Column(
children: <Widget>[
new Flexible(
child: new ListView.builder(
itemBuilder: (context,index){
return _buildToDoItem(_todoItems[index], index);
},
),
),
new Expanded(
child: ReorderableListView(
children: <Widget>[
for(final widget in _todoItems)
for(final widget1 in WidgetList)
GestureDetector(
key: Key(widget.todoText),
child: Dismissible(
key: Key(widget.todoText),
child: CheckboxListTile(
value: widget1._todoCheck,
title: _strikeThrough(widget.todoText,widget1._todoCheck),
onChanged: (checkValue){
setState(() {
if(!checkValue){
widget1._todoCheck = false;
}
else{
widget1._todoCheck = true;
}
});
}),
background: Container(
child: Icon(Icons.delete),
alignment: Alignment.centerRight,
color: Colors.orange[300],
),
confirmDismiss: (dismissDirection){
return showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context){
return AlertDialog(
title: Text("Delete Todo?"),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: (){
Navigator.of(context).pop(true);
},
),
FlatButton(
child: Text("Cancel"),
onPressed: (){
Navigator.of(context).pop(true);
},),
],
);
});
},
direction: DismissDirection.endToStart,
movementDuration: const Duration(milliseconds: 200),
onDismissed: (dismissedDirection){
WidgetList.remove(widget);
Fluttertoast.showToast(
msg: "Todo Deleted!"
);
},
),
onDoubleTap: (){
popUpTextController.text = widget.todoText;
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context){
return AlertDialog(
title: Text("Edit ToDo"),
content: TextFormField(
controller: popUpTextController,
),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: (){
setState(() {
widget.todoText=popUpTextController.text;
});
Navigator.of(context).pop(true);
},
),
FlatButton(
child: Text("Cancel"),
onPressed: (){
Navigator.of(context).pop(false);
},
)
],
);
}
);
},
)
],
onReorder: (oldIndex, newIndex){
setState(() {
if(oldIndex < newIndex){
newIndex -= 1;
}
var replaceWiget = WidgetList.removeAt(oldIndex);
WidgetList.insert(newIndex, replaceWiget);
});
},)
)
],
);
}
Widget _buildToDoItem(String todoText, int index){
return new ListTile(
title: new Text(todoText),
onLongPress: () => _promptRemoveToDoItem(index),
);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('To Do List')
),
body:_buildToDoList(context),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: new FloatingActionButton(
focusElevation: 7,
onPressed:_pushAddToDoScreen,
tooltip: 'Add Task',
child: new Icon(Icons.add)
),
drawer: Drawer(
child: ListView(
padding: const EdgeInsets.all(0),
children: <Widget>[
DrawerHeader(
child: Text('To Do App Drawer Header'),
decoration: BoxDecoration(
color: Colors.teal,
)
),
ListTile(
title: Text('Add Items'),
onTap: _pushAddToDoScreen,
),
ListTile(
title: Text('Remove Items'),
subtitle: Text('Long Press on any item to remove it'),
onTap: (){
Navigator.pop(context);
},
),
ListTile(
title: Text('Check Status of Items'),
subtitle: Text('If the item is in List, then it is Pending, else long press to mark it done'),
onTap: (){
Navigator.pop(context);
},
),
],
)
),
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {},
)
]
)
)
);
}
void _pushAddToDoScreen(){
Navigator.of(context).push(
new MaterialPageRoute(
builder: (context){
return new Scaffold(
appBar: new AppBar(
title: new Text('Add a new task')
),
body: new TextField(
autofocus: true,
onSubmitted: (val){
_addToDoItem(val);
Navigator.pop(context);
},
decoration: new InputDecoration(
hintText: 'Enter Tasks....',
contentPadding: const EdgeInsets.all(16.0)
),
)
);
}
)
);
}
}