Related
I have this error in the onPressed :() function of the login: The method 'userLogin' is not defined for the type 'BottomSheet'.
Could anyone help me solve? Thank you.
CustomButton(
label: "LOGIN",
primaryColor: Theme.of(context).primaryColor,
secondaryColor: Colors.white,
onPressed: () => {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState.validate()) {
userLogin()}
},
),
Here is the complete code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:login_ui/home2.dart';
import 'clipper.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
TextEditingController _emailController,
_passwordController,
_nameController = TextEditingController();
//
bool _visible = false;
Future userLogin() async {
//Login API URL
//use your local IP address instead of localhost or use Web API
String url = "https://www.toptradeitaly.com/login_api/user_login.php";
// Showing LinearProgressIndicator.
setState(() {
_visible = true;
});
// Getting username and password from Controller
var data = {
'username': _emailController.text,
'password': _passwordController.text,
};
//Starting Web API Call.
var response = await http.post(url, body: json.encode(data));
if (response.statusCode == 200) {
//Server response into variable
print(response.body);
var msg = jsonDecode(response.body);
//Check Login Status
if (msg['loginStatus'] == true) {
setState(() {
//hide progress indicator
_visible = false;
});
// Navigate to Home Screen
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
HomePage(uname: msg['userInfo']['NAME'])));
} else {
setState(() {
//hide progress indicator
_visible = false;
//Show Error Message Dialog
showMessage(msg["message"]);
});
}
} else {
setState(() {
//hide progress indicator
_visible = false;
//Show Error Message Dialog
showMessage("Error during connecting to Server.");
});
}
}
Future<dynamic> showMessage(String _msg) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(_msg),
actions: <Widget>[
TextButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
//
#override
Widget build(BuildContext context) {
//GO logo widget
Widget logo() {
return Padding(
padding:
EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.15),
child: Container(
width: MediaQuery.of(context).size.width,
height: 220,
child: Stack(
children: <Widget>[
Positioned(
child: Container(
child: Align(
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.white),
width: 150,
height: 150,
),
),
height: 154,
)),
Positioned(
child: Container(
height: 154,
child: Align(
child: Text(
"GO",
style: TextStyle(
fontSize: 120,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor,
),
),
)),
),
Positioned(
width: MediaQuery.of(context).size.width * 0.15,
height: MediaQuery.of(context).size.width * 0.15,
bottom: MediaQuery.of(context).size.height * 0.046,
right: MediaQuery.of(context).size.width * 0.22,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.white),
),
),
Positioned(
width: MediaQuery.of(context).size.width * 0.08,
height: MediaQuery.of(context).size.width * 0.08,
bottom: 0,
right: MediaQuery.of(context).size.width * 0.32,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.white),
),
),
],
),
),
);
}
void _loginSheet(context) {
showBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return BottomSheet(
emailController: _emailController,
passwordController: _passwordController,
);
},
);
}
void _registerSheet(context) {
showBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return BottomSheet(
emailController: _emailController,
passwordController: _passwordController,
nameController: _nameController,
);
},
);
}
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Theme.of(context).primaryColor,
body: Builder(builder: (context) {
return Column(
children: <Widget>[
logo(),
Padding(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
CustomButton(
label: "LOGIN",
primaryColor: Colors.white,
secondaryColor: Theme.of(context).primaryColor,
onPressed: () => _loginSheet(context),
),
SizedBox(height: 20),
CustomButton(
label: "REGISTER",
primaryColor: Theme.of(context).primaryColor,
secondaryColor: Colors.white,
onPressed: () => _registerSheet(context),
),
],
),
padding: EdgeInsets.only(top: 80, left: 20, right: 20),
),
Expanded(
child: Align(
child: ClipPath(
child: Container(
color: Colors.white,
height: 300,
),
clipper: BottomWaveClipper(),
),
alignment: Alignment.bottomCenter,
),
)
],
crossAxisAlignment: CrossAxisAlignment.stretch,
);
}),
);
}
}
class CustomButton extends StatelessWidget {
final Color primaryColor;
final Color secondaryColor;
final String label;
final Function() onPressed;
const CustomButton({
Key key,
this.primaryColor,
this.secondaryColor,
#required this.label,
this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return SizedBox(
height: 50,
width: double.infinity,
child: RaisedButton(
highlightElevation: 0.0,
splashColor: secondaryColor,
highlightColor: primaryColor,
elevation: 0.0,
color: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0),
side: BorderSide(color: Colors.white, width: 3)),
child: Text(
label,
style: TextStyle(
fontWeight: FontWeight.bold, color: secondaryColor, fontSize: 20),
),
onPressed: onPressed,
),
);
}
}
class CustomTextField extends StatelessWidget {
final Icon icon;
final String hint;
final TextEditingController controller;
final bool obsecure;
const CustomTextField({
this.controller,
this.hint,
this.icon,
this.obsecure,
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextField(
controller: controller,
obscureText: obsecure ?? false,
style: TextStyle(
fontSize: 20,
),
decoration: InputDecoration(
hintStyle: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
hintText: hint,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: BorderSide(
color: Theme.of(context).primaryColor,
width: 2,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: BorderSide(
color: Theme.of(context).primaryColor,
width: 3,
),
),
prefixIcon: Padding(
child: IconTheme(
data: IconThemeData(color: Theme.of(context).primaryColor),
child: icon,
),
padding: EdgeInsets.only(left: 30, right: 10),
)),
);
}
}
//
final _formKey = GlobalKey<FormState>();
//
class BottomSheet extends StatelessWidget {
const BottomSheet({
Key key,
#required TextEditingController emailController,
#required TextEditingController passwordController,
TextEditingController nameController,
}) : _emailController = emailController,
_passwordController = passwordController,
_nameController = nameController,
super(key: key);
final TextEditingController _emailController;
final TextEditingController _passwordController;
final TextEditingController _nameController;
List<Widget> get _registerLogo => [
Positioned(
child: Container(
padding: EdgeInsets.only(bottom: 25, right: 40),
child: Text(
"REGI",
style: TextStyle(
fontSize: 38,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
alignment: Alignment.center,
),
),
Positioned(
child: Align(
child: Container(
padding: EdgeInsets.only(top: 40, left: 28),
width: 130,
child: Text(
"STER",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 38),
),
),
alignment: Alignment.center,
),
),
];
List<Widget> get _loginLogo => [
Align(
alignment: Alignment.center,
child: Container(
child: Text(
"LOGIN",
style: TextStyle(
fontSize: 42,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
alignment: Alignment.center,
),
),
];
#override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(color: Theme.of(context).canvasColor),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(40.0), topRight: Radius.circular(40.0)),
child: Container(
child: ListView(
children: <Widget>[
Container(
child: Stack(
children: <Widget>[
Positioned(
left: 10,
top: 10,
child: IconButton(
onPressed: () {
Navigator.of(context).pop();
_emailController.clear();
_passwordController.clear();
_nameController?.clear();
},
icon: Icon(
Icons.close,
size: 30.0,
color: Theme.of(context).primaryColor,
),
),
)
],
),
height: 50,
width: 50,
),
//
Form(
key: _formKey,
child:
//
SingleChildScrollView(
child: Padding(
padding: EdgeInsets.only(left: 20, right: 20),
child: Column(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: 140,
child: Stack(
children: <Widget>[
Align(
child: Container(
width: 130,
height: 130,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).primaryColor),
),
alignment: Alignment.center,
),
..._nameController != null
? _registerLogo
: _loginLogo
],
),
),
SizedBox(height: 60),
if (_nameController != null)
CustomTextField(
controller: _nameController,
hint: "NAME",
icon: Icon(Icons.person),
),
SizedBox(height: 20),
CustomTextField(
controller: _emailController,
hint: "EMAIL",
icon: Icon(Icons.email),
),
SizedBox(height: 20),
CustomTextField(
controller: _passwordController,
hint: "PASSWORD",
icon: Icon(Icons.lock),
obsecure: true,
),
SizedBox(height: 20),
if (_nameController != null)
CustomButton(
label: "REGISTER",
primaryColor: Theme.of(context).primaryColor,
secondaryColor: Colors.white,
onPressed: () {},
),
SizedBox(height: 20),
if (_nameController == null)
CustomButton(
label: "LOGIN",
primaryColor: Theme.of(context).primaryColor,
secondaryColor: Colors.white,
onPressed: () => {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState.validate()) {
userLogin()}
},
),
SizedBox(height: 20),
],
),
),
),),
],
),
height: MediaQuery.of(context).size.height / 1.1,
width: MediaQuery.of(context).size.width,
color: Colors.white,
),
),
);
}
}
I am working on a flutter project, so I was trying to use Flutter bloc state management. I built everything from state to event and also the bloc thing. like the code below:
State:
abstract class UserState extends Equatable {
const UserState();
#override
List<Object> get props => [];
}
class UserInitialState extends UserState {}
class UserLoadingState extends UserState {
#override
List<Object> get props => [];
}
class UserLoadedState extends UserState {
final UserModel user;
const UserLoadedState(this.user);
#override
List<Object> get props => [user];
}
class UserValidationState extends UserState {
final Map<String, dynamic> validation;
const UserValidationState(this.validation);
#override
List<Object> get props => [validation];
}
class UserErrorState extends UserState {
final Map<String, dynamic> error;
const UserErrorState(this.error);
#override
List<Object> get props => [error];
}
Event:
class LoginUserEvent extends UserEvent {
final String email, password;
const LoginUserEvent({this.email, this.password});
#override
List<Object> get props => [];
}
Bloc:
UserBloc(this._userReprository) : super(UserInitialState()) {
on<LoginUserEvent>((event, emit) async {
emit(UserLoadingState());
try {
await _userReprository
.loginUser(
email: event.email,
password: event.password,
)
.then((result) {
if (result.contains('success')) {
emit(UserLoadedState(result[1]));
} else if (result.contains('errors')) {
emit(UserValidationState(result[1]));
}
});
} catch (e) {
emit(UserErrorState(e));
}
});
}
so it should work like when I click on the button it switch to loading event then after response it either load or throw a validation error or it catch an error if something happened.
well unfortunately the UI is not updating immedtialy, only after a second refresh or a setstate of another way or something that rebuild it it only change
Here is the code of the UI:
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
// Clearing all TextEdititng Controller Values
_clearValues() {
_email.clear();
_password.clear();
_valErrors.clear();
}
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
margin: EdgeInsets.only(top: deviceSize.height * 0.05),
child: Column(
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 20),
child: Material(
shadowColor: Colors.black54,
color: Theme.of(context).colorScheme.primary,
elevation: 2,
borderRadius: BorderRadius.circular(35),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 15,
),
child: SvgPicture.asset(
'./assets/img/auth_logo.svg',
width: deviceSize.width * 0.5,
height: deviceSize.width * 0.5,
),
),
),
),
const SizedBox(
height: 10,
),
Column(
children: <Widget>[
Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
child: Material(
borderRadius: BorderRadius.circular(25),
elevation: 2,
shadowColor: Colors.black54,
child: TextField(
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
controller: _email,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.email),
border: OutlineInputBorder(
borderSide: const BorderSide(
color: Colors.white, width: 32.0),
borderRadius: BorderRadius.circular(25.0),
),
labelText: 'Email',
),
),
),
),
_valErrors.containsKey('email')
? Column(
children: <Widget>[
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 30,
vertical: 1,
),
child: Text(
_valErrors['email'],
textAlign: TextAlign.start,
style: const TextStyle(
color: Colors.red,
),
),
),
),
const SizedBox(
height: 15,
)
],
)
: const SizedBox(
height: 25,
),
],
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
child: Material(
elevation: 2,
shadowColor: Colors.black54,
borderRadius: BorderRadius.circular(25),
child: TextField(
textInputAction: TextInputAction.done,
obscureText: hiddenContent,
controller: _password,
keyboardType: TextInputType.text,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.lock),
suffixIcon: IconButton(
icon: hiddenContent
? const Icon(Icons.visibility)
: const Icon(Icons.visibility_off),
onPressed: () {
setState(() {
hiddenContent = !hiddenContent;
});
}),
border: OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.white, width: 32.0),
borderRadius: BorderRadius.circular(25.0),
),
labelText: 'Password',
),
),
),
),
_valErrors.containsKey('password')
? Column(
children: <Widget>[
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 30, vertical: 1),
child: Text(
_valErrors['password'],
textAlign: TextAlign.start,
style: const TextStyle(
color: Colors.red,
),
),
),
),
const SizedBox(
height: 15,
)
],
)
: const SizedBox(
height: 25,
),
BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
if (state is UserLoadingState) {
return const CircularProgressIndicator();
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Material(
elevation: 2,
shadowColor: Colors.black54,
borderRadius: BorderRadius.circular(25),
child: TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all<EdgeInsets>(
const EdgeInsets.symmetric(
vertical: 15,
horizontal: 30,
),
),
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
),
backgroundColor: MaterialStateProperty.all<Color>(
Theme.of(context).colorScheme.primary,
),
),
onPressed: () async {
// If Statement to make sure THe validation is correct and to show Messages
setState(() {
if (_email.text.isEmpty) {
_valErrors['email'] = 'This Field is Required';
} else {
_valErrors.remove('email');
}
if (_password.text.isEmpty) {
_valErrors['password'] =
'This Field is Required';
} else if (_password.text.length < 8) {
_valErrors['password'] =
'Password must be Greater Than 8';
} else {
_valErrors.remove('password');
}
});
// Databse Function Login
if (_valErrors.isEmpty) {
context.read<UserBloc>().add(
LoginUserEvent(
email: _email.text,
password: _password.text,
),
);
if (state is UserLoadedState) {
_clearValues();
Navigator.of(context)
.pushReplacementNamed('/botnavbar');
} else if (state is UserValidationState) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text(
'There is some problems'),
content: Text(state
.validation['message']
.toString()),
actions: <Widget>[
TextButton(
onPressed: () =>
Navigator.of(context).pop(),
child: const Text('Okay'),
),
],
);
});
}
}
},
child: const Text(
'Login',
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
),
),
),
Material(
elevation: 2,
shadowColor: Colors.black54,
borderRadius: BorderRadius.circular(25),
child: TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all<EdgeInsets>(
const EdgeInsets.symmetric(
vertical: 15,
horizontal: 35,
),
),
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
side: BorderSide(
color: Theme.of(context).colorScheme.primary,
),
),
),
),
onPressed: () {
Navigator.of(context)
.pushReplacementNamed('/register');
},
child: Text(
'Register',
style: TextStyle(
fontSize: 20,
color: Theme.of(context).colorScheme.primary,
),
),
),
),
],
);
},
),
],
),
),
),
);
}
I tried to use the blocbuilder on the whole scaffold but still the same problem
Hope you can help me guys
i'm stuck on this problem for long time
in debug console
i don't know how to fix it please help
it says flexible while i did not use flexible widget.
i only used expanded and even when i remove expanded widget still have the same error
also i tried to remove everything except the list view builder widget i sill get the same error
this is the code:
import 'package:flutter/material.dart';
import 'package:todoy/Colors.dart';
import 'Taskslistview.dart';
import 'TasksListTile.dart';
import 'package:provider/provider.dart';
import 'Helper.dart';
bool isChekec = false;
class TasksScreen extends StatelessWidget {
Widget buildsheet(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
),
height: 400,
child: Padding(
padding: EdgeInsets.only(left: 50.0, right: 50),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
height: 10,
),
Center(
child: Text(
'Add Task',
style: TextStyle(
fontSize: 30,
color: Provider.of<Helper>(context).choosedcolor),
)),
TextField(
decoration: InputDecoration(
helperStyle: TextStyle(color: Colors.red),
helperText: Provider.of<Helper>(context).alreadyexist
? "this task already exist"
: " "),
textAlign: TextAlign.center,
onChanged: (value) {
if (Provider.of<Helper>(context, listen: false)
.tasknames
.contains(value)) {
Provider.of<Helper>(context, listen: false)
.alreadyture();
} else {
Provider.of<Helper>(context, listen: false)
.alreadyfalse();
}
Provider.of<Helper>(context, listen: false)
.addtaskname(value);
Provider.of<Helper>(context, listen: false)
.addtaskname(value);
print(Provider.of<Helper>(context, listen: false)
.addedtask);
},
),
SizedBox(
height: 10,
),
Container(
height: 50,
// ignore: deprecated_member_use
child: FlatButton(
color: Provider.of<Helper>(context).choosedcolor,
onPressed: () {
if (Provider.of<Helper>(context, listen: false)
.alreadyexist) {
print("you cant");
} else if (Provider.of<Helper>(context, listen: false)
.addedtask ==
"") {
print("ffgg");
} else {
Provider.of<Helper>(context, listen: false).addname(
Provider.of<Helper>(context, listen: false)
.addedtask);
Provider.of<Helper>(context, listen: false)
.add(TasksListTile(
isChcked: isChekec,
nameofthetask:
Provider.of<Helper>(context, listen: false)
.addedtask,
));
Provider.of<Helper>(context, listen: false)
.addedtask = "";
Navigator.pop(context);
}
},
child: Text(
"ADD",
style: TextStyle(color: Colors.white, fontSize: 20),
)),
)
],
),
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Provider.of<Helper>(context).choosedcolor,
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.only(left: 300.0, top: 30, right: 15),
child: GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ColorsScreen()));
},
child: CircleAvatar(
radius: 35,
backgroundColor: Colors.white,
child: Icon(
Icons.invert_colors,
size: 40,
color: Provider.of<Helper>(context).choosedcolor,
)),
),
),
Padding(
padding: EdgeInsets.only(left: 40.0, top: 40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CircleAvatar(
radius: 35,
backgroundColor: Colors.white,
child: Icon(
Icons.list,
size: 40,
color: Provider.of<Helper>(context).choosedcolor,
)),
Padding(
padding: const EdgeInsets.only(right: 30.0),
child: Container(
height: 67,
child: FittedBox(
child: FloatingActionButton(
child: Icon(Icons.delete_forever,
size: 40,
color: Provider.of<Helper>(context)
.choosedcolor),
backgroundColor: Colors.white,
onPressed: () {
Provider.of<Helper>(context, listen: false)
.remove();
},
),
),
),
),
],
),
),
Padding(
padding: EdgeInsets.only(top: 8.0, left: 10, bottom: 10),
child: Text(
"Todoy",
style: TextStyle(color: Colors.white, fontSize: 40),
textAlign: TextAlign.start,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
' ${Provider.of<Helper>(context).tasks.length} Tasks',
style: TextStyle(color: Colors.white, fontSize: 40),
textAlign: TextAlign.start,
),
Padding(
padding: const EdgeInsets.only(right: 30.0),
child: Container(
height: 70,
child: FittedBox(
child: FloatingActionButton(
child: Text(
"+",
style: TextStyle(
fontSize: 50,
color: Provider.of<Helper>(context)
.choosedcolor),
),
backgroundColor: Colors.white,
onPressed: () {
showModalBottomSheet(
context: context, builder: buildsheet);
},
),
),
),
),
],
),
],
),
),
SizedBox(
height: 20,
),
Expanded(
child: Container(
padding: EdgeInsets.only(left: 20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(40),
topRight: Radius.circular(40))),
child: Taskslistview(),
),
),
],
),
),
);
}
}
inside Tasklistview:
class Taskslistview extends StatelessWidget {
#override
Widget build(BuildContext context) {
bool isChecked = false;
return ListView.builder(
itemCount: Provider.of<Helper>(context).tasks.length,
itemBuilder: (context, index) {
return TasksListTile(
isChcked: isChecked,
nameofthetask:
Provider.of<Helper>(context, listen: false).tasknames[index],
);
},
);
}
}
please help i tried everything to fix it nothing worked
I have been trying to add an edit function to my to do list in which a user can select the item the user wants to edit, then that should pop a dialog where there is a textfield to enter the new value of the selected item and a Button that saves the changes. Currently I have a function that calls the array where the tasks are stored then it is supposed to triger the selected item using index so that at the end that selected value could be given a new value when onPressed, see this edit functionality as instagrams one except it edits text.
The problem comes when calling that function into the dialog's edit button because I am doing it like this onPressed: () => _editToDoItem(_controller.text, index) and since I have to pass 2 parameters there, the error I am getting is Undefined name 'index'. How can this problem be solved to make this edit function work?. By the way, I haven't get to try the edit function because of this error so please correct me if the function or any part of the code is incorrect.
everything to do with the edit function below.
List<ToDoElement> _toDoItems = [];
TextEditingController _controller = TextEditingController();
// this function adds a task to the list
void _addToDoItem(String task) {
if(task.isNotEmpty) {
setState(() {
_toDoItems.add(ToDoElement(task, DateTime.now()));
});
}
}
// this is the function that is supposed to edit the selected index from the _toDoItems array
void _editToDoItem(String newText, int index) {
setState(() {
_toDoItems[index].task = newText;
});
}
_editDialog(BuildContext context) {
return showDialog(context: context, builder: (context) {
return Dialog(
child: Container(
height: 180,
width: 100,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 60,
child: TextField(
controller: _controller,
autofocus: true,
style: TextStyle(fontSize: 18,),
)
),
Container(
height: 65,
width: double.infinity,
margin: EdgeInsets.only(top: 5,),
child: RaisedButton(
textColor: Colors.white,
color: Colors.red,
child: Text('EDIT'),
onPressed: () {
_editToDoItem(_controller.text, index); // error on index, Undefined name 'index'
FocusScope.of(context).requestFocus(FocusNode());
},
),
),
],
),
),
);
});
}
full main.dart file
class ToDoElement {
String task;
final DateTime timeOfCreation;
ToDoElement(this.task, this.timeOfCreation);
}
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatefulWidget {
#override
createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
List<ToDoElement> _toDoItems = [];
TextEditingController _controller = TextEditingController();
void _addToDoItem(String task) {
if(task.isNotEmpty) {
setState(() {
_toDoItems.add(ToDoElement(task, DateTime.now()));
});
}
}
void _editToDoItem(String newText, int index) {
setState(() {
_toDoItems[index].task = newText;
});
}
void _removeTodoItem(int index) {
setState(() => _toDoItems.removeAt(index));
}
_editDialog(BuildContext context) {
return showDialog(context: context, builder: (context) {
return Dialog(
backgroundColor: Colors.transparent,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(20.0)),
),
padding: EdgeInsets.all(20),
height: 180,
width: 100,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 60,
child: TextField(
controller: _controller,
autofocus: true,
/*onSubmitted: (val) {
_addToDoItem(val);
_controller.clear();
},*/
style: TextStyle(fontSize: 18,),
decoration: InputDecoration(
hintText: 'Add a task here...',
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: BorderSide(color: Colors.red, width: 2),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: BorderSide(color: Colors.red, width: 2),
),
),
)
),
Container(
height: 65,
width: double.infinity,
margin: EdgeInsets.only(top: 5,),
child: RaisedButton(
textColor: Colors.white,
color: Colors.red,
child: Text('EDIT', style: TextStyle(fontSize: 18)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
onPressed: () {
_editToDoItem(_controller.text, index);
FocusScope.of(context).requestFocus(FocusNode());
},
),
),
],
),
),
);
});
}
Widget _buildToDoItem(String toDoText, int index) {
return SizedBox(
child: Container(
height: 58,
margin: EdgeInsets.only(left: 22.0, right: 22.0, bottom: 12,),
decoration: BoxDecoration(
border: Border.all(width: 1.5, color: Colors.red),
borderRadius: BorderRadius.all(Radius.circular(18)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children:[
Expanded(
child: ListTile(
title: Text(
toDoText,
style: TextStyle(fontSize: 18),
),
onTap: () => null,
),
),
FlatButton(
child: Text('Edit', style: TextStyle(color: Colors.red, fontSize: 16.5),),
onPressed: () => _editDialog(context),
),
FlatButton(
child: Text('Delete', style: TextStyle(color: Colors.red, fontSize: 16.5),),
onPressed: () => _removeTodoItem(index),
),
],
),
),
);
}
int compareElement(ToDoElement a, ToDoElement b) =>
a.timeOfCreation.isAfter(b.timeOfCreation) ? -1 : 1;
Widget _buildToDoList() {
_toDoItems.sort(compareElement);
return Expanded(
child: ListView.builder(
itemCount: _toDoItems.length,
itemBuilder: (context, index) {
if (index < _toDoItems.length) {
return _buildToDoItem(_toDoItems[index].task, index);
}
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50),
child: AppBar(
centerTitle: true,
backgroundColor: Colors.red,
title: Text('To Do List', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold,),),
)
),
backgroundColor: Colors.white,
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 60,
margin: EdgeInsets.all(22),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 10,
child: Container(
height: double.infinity,
child: TextField(
controller: _controller,
autofocus: true,
onSubmitted: (val) {
_addToDoItem(val);
_controller.clear();
},
style: TextStyle(fontSize: 18,),
decoration: InputDecoration(
hintText: 'Add a task here...',
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: BorderSide(color: Colors.red, width: 2),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: BorderSide(color: Colors.red, width: 2),
),
),
),
),
),
Expanded(
flex: 4,
child: Container(
height: double.infinity,
margin: EdgeInsets.only(left: 12),
child: RaisedButton(
textColor: Colors.white,
color: Colors.red,
child: Text('ADD', style: TextStyle(fontSize: 18)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
onPressed: () {
_addToDoItem(_controller.text);
_controller.clear();
FocusScope.of(context).requestFocus(FocusNode());
},
),
),
),
],
),
),
_buildToDoList()
]
),
),
);
}
}
If you have any questions please let me know in the comments;)
You can copy paste run full code below
You can provide index to _editDialog then _editToDoItem can get index
code snippet
_editDialog(BuildContext context, int index)
...
FlatButton(
child: Text(
'Edit',
style: TextStyle(color: Colors.red, fontSize: 16.5),
),
onPressed: () => _editDialog(context, index),
),
working demo
full code
import 'package:flutter/material.dart';
class ToDoElement {
String task;
final DateTime timeOfCreation;
ToDoElement(this.task, this.timeOfCreation);
}
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatefulWidget {
#override
createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
List<ToDoElement> _toDoItems = [];
TextEditingController _controller = TextEditingController();
TextEditingController _controller1 = TextEditingController();
void _addToDoItem(String task) {
if (task.isNotEmpty) {
setState(() {
_toDoItems.add(ToDoElement(task, DateTime.now()));
});
}
}
void _editToDoItem(String newText, int index) {
setState(() {
_toDoItems[index].task = newText;
});
}
void _removeTodoItem(int index) {
setState(() => _toDoItems.removeAt(index));
}
_editDialog(BuildContext context, int index) {
return showDialog(
context: context,
builder: (context) {
return Dialog(
backgroundColor: Colors.transparent,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(20.0)),
),
padding: EdgeInsets.all(20),
height: 180,
width: 100,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 60,
child: TextField(
controller: _controller,
autofocus: true,
/*onSubmitted: (val) {
_addToDoItem(val);
_controller.clear();
},*/
style: TextStyle(
fontSize: 18,
),
decoration: InputDecoration(
hintText: 'Add a task here...',
enabledBorder: OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(12.0)),
borderSide: BorderSide(color: Colors.red, width: 2),
),
focusedBorder: OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(12.0)),
borderSide: BorderSide(color: Colors.red, width: 2),
),
),
)),
Container(
height: 65,
width: double.infinity,
margin: EdgeInsets.only(
top: 5,
),
child: RaisedButton(
textColor: Colors.white,
color: Colors.red,
child: Text('EDIT', style: TextStyle(fontSize: 18)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
onPressed: () {
_editToDoItem(_controller.text, index);
FocusScope.of(context).requestFocus(FocusNode());
},
),
),
],
),
),
);
});
}
Widget _buildToDoItem(String toDoText, int index) {
return SizedBox(
child: Container(
height: 58,
margin: EdgeInsets.only(
left: 22.0,
right: 22.0,
bottom: 12,
),
decoration: BoxDecoration(
border: Border.all(width: 1.5, color: Colors.red),
borderRadius: BorderRadius.all(Radius.circular(18)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: ListTile(
title: Text(
toDoText,
style: TextStyle(fontSize: 18),
),
onTap: () => null,
),
),
FlatButton(
child: Text(
'Edit',
style: TextStyle(color: Colors.red, fontSize: 16.5),
),
onPressed: () => _editDialog(context, index),
),
FlatButton(
child: Text(
'Delete',
style: TextStyle(color: Colors.red, fontSize: 16.5),
),
onPressed: () => _removeTodoItem(index),
),
],
),
),
);
}
int compareElement(ToDoElement a, ToDoElement b) =>
a.timeOfCreation.isAfter(b.timeOfCreation) ? -1 : 1;
Widget _buildToDoList() {
_toDoItems.sort(compareElement);
return Expanded(
child: ListView.builder(
itemCount: _toDoItems.length,
itemBuilder: (context, index) {
if (index < _toDoItems.length) {
return _buildToDoItem(_toDoItems[index].task, index);
}
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50),
child: AppBar(
centerTitle: true,
backgroundColor: Colors.red,
title: Text(
'To Do List',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
)),
backgroundColor: Colors.white,
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
Container(
height: 60,
margin: EdgeInsets.all(22),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 10,
child: Container(
height: double.infinity,
child: TextField(
controller: _controller1,
autofocus: true,
onSubmitted: (val) {
_addToDoItem(val);
_controller1.clear();
},
style: TextStyle(
fontSize: 18,
),
decoration: InputDecoration(
hintText: 'Add a task here...',
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: BorderSide(color: Colors.red, width: 2),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: BorderSide(color: Colors.red, width: 2),
),
),
),
),
),
Expanded(
flex: 4,
child: Container(
height: double.infinity,
margin: EdgeInsets.only(left: 12),
child: RaisedButton(
textColor: Colors.white,
color: Colors.red,
child: Text('ADD', style: TextStyle(fontSize: 18)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
onPressed: () {
_addToDoItem(_controller1.text);
_controller1.clear();
FocusScope.of(context).requestFocus(FocusNode());
},
),
),
),
],
),
),
_buildToDoList()
]),
),
);
}
}
I am using the flutter show search() and a class extended by SearchDelegate.
i pass a list of my class objects to the delegate class. In the buildResult() method, i need to create a custom list view. each list view attribute needs to change its underlying widgets. but i can not use setstate() in the buildResults() function since it does not have a builder.
here is my code.hope it all makes sense. just FYI its a simple search for product for online store.
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:rashan/models/order.dart';
import 'package:rashan/models/product.dart';
class ProductScreen extends StatefulWidget {
static final String id = "product_screen";
final String category;
final String subCategory;
ProductScreen({
#required this.category,
#required this.subCategory,
});
#override
_ProductScreenState createState() => _ProductScreenState();
}
class _ProductScreenState extends State<ProductScreen> {
final productList = <Product>[];
#override
void initState() {
super.initState();
FirebaseDatabase.instance
.reference()
.child("Data")
.child("All Products")
.child(widget.category)
.child(widget.subCategory)
.once()
.then((snapshot) {
setState(() {
var result = snapshot.value.values as Iterable;
for (var item in result) {
var product = Product(
id: item['id'],
name: item['name'],
image: item['image'],
price: item['price'],
quantity: item['quantity'],
category: item['category'],
subCategory: item['subCategory']);
productList.add(product);
}
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
centerTitle: true,
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.black,
),
onPressed: () {
Navigator.pop(context);
},
),
title: Text(
"Rashan",
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 48.0,
fontFamily: "Billabong"),
),
actions: <Widget>[
productList.length != 0
? IconButton(
icon: Icon(
Icons.search,
color: Colors.black,
),
onPressed: () {
showSearch(
context: context, delegate: ProductSearch(productList));
},
)
: Container()
],
),
body: Center(
child: Container(
child: productList.length == 0
? Text(
"Loading Products ...!",
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 16.0,
fontWeight: FontWeight.w500),
)
: ListView.builder(
itemCount: productList.length,
itemBuilder: (BuildContext context, int index) {
return productUI(productList[index]);
},
)),
),
);
}
Widget productUI(Product product) {
String _price = product.price;
var order = new Order();
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
color: Colors.white,
elevation: 5.0,
margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: Container(
padding: EdgeInsets.all(5.0),
child: Row(
children: <Widget>[
Container(
height: 120.0,
width: 120.0,
child: Image.network(product.image),
),
SizedBox(
width: 10.0,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 5.0),
padding: EdgeInsetsDirectional.only(end: 4.0),
child: Text(
product.name,
style: TextStyle(
fontSize: 20.0, fontWeight: FontWeight.w600),
),
),
SizedBox(
height: 10.0,
),
Text(product.category),
Text(product.subCategory),
Text(
'Price: Rs $_price ',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 16.0,
fontWeight: FontWeight.w500),
),
SizedBox(
height: 10.0,
),
Container(
margin:
EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
height: 40.0,
color: Theme.of(context).primaryColor,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
order.decrementQuantity(product.id);
});
},
child: Container(
height: 50.0,
width: 50.0,
child: Icon(
Icons.remove,
color: Colors.white,
),
),
),
Expanded(
child: Center(
child: Text(
order.getQuantity(product.id) == 0
? "Add to Cart"
: order.getQuantity(product.id).toString(),
style: TextStyle(color: Colors.white),
)),
),
GestureDetector(
onTap: () {
setState(() {
order.addOrIncrement(product.id);
});
},
child: Container(
height: 50.0,
width: 50.0,
child: Icon(
Icons.add,
color: Colors.white,
),
),
)
],
),
)
],
),
)
],
),
),
);
}
}
class ProductSearch extends SearchDelegate<Product> {
List<Product> _productList;
ProductSearch(this._productList);
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(
Icons.clear,
),
onPressed: () {
query = '';
})
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(
Icons.arrow_back,
),
onPressed: () {
close(context, null);
});
}
#override
Widget buildResults(BuildContext context) {
List<Product> searchResult = [];
for (var product in _productList) {
if (query.isNotEmpty && product.name.toLowerCase().contains(query)) {
searchResult.add(product);
print(product.name);
}
}
return Container(
child: ListView.builder(
itemCount: searchResult.length,
itemBuilder: (BuildContext context, int index) {
return productUI(context, searchResult[index]);
},
),
);
}
#override
Widget buildSuggestions(BuildContext context) {
return Container();
}
Widget productUI(BuildContext context, Product product) {
String _price = product.price;
var order = new Order();
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
color: Colors.white,
elevation: 5.0,
margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: Container(
padding: EdgeInsets.all(5.0),
child: Row(
children: <Widget>[
Container(
height: 120.0,
width: 120.0,
child: Image.network(product.image),
),
SizedBox(
width: 10.0,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 5.0),
padding: EdgeInsetsDirectional.only(end: 4.0),
child: Text(
product.name,
style: TextStyle(
fontSize: 20.0, fontWeight: FontWeight.w600),
),
),
SizedBox(
height: 10.0,
),
Text(product.category),
Text(product.subCategory),
Text(
'Price: Rs $_price ',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 16.0,
fontWeight: FontWeight.w500),
),
SizedBox(
height: 10.0,
),
Container(
margin:
EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
height: 40.0,
color: Theme.of(context).primaryColor,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () {
// setState(() {
order.decrementQuantity(product.id);
// });
},
child: Container(
height: 50.0,
width: 50.0,
child: Icon(
Icons.remove,
color: Colors.white,
),
),
),
Expanded(
child: Center(
child: Text(
order.getQuantity(product.id) == 0
? "Add to Cart"
: order.getQuantity(product.id).toString(),
style: TextStyle(color: Colors.white),
)),
),
GestureDetector(
onTap: () {
// setState(() {
order.addOrIncrement(product.id);
// });
},
child: Container(
height: 50.0,
width: 50.0,
child: Icon(
Icons.add,
color: Colors.white,
),
),
)
],
),
)
],
),
)
],
),
),
);
}
}
If you want to have a state for each list item, you can just create a StatefulWidget instead of using the method productUI(). If you want to control the state from the upper Widget (List), then just create a StatefulWidget for that level.
class ProductUi extends StatefulWidget {
final Product product;
ProductScreen(#required this.product) : assert(product != null);
#override
_ProductUiState createState() => _ProductUiState();
}
class _ProductUiState extends State<ProductUi> {
#override
Widget build(BuildContext context) => productUI(context, widget.product);
Widget productUI(BuildContext context, Product product) {
// ...
// now you can setState from taps and delegates
}
}