I want to create a list of custom checkboxes(custom shape and color + icon) in flutter which can change their state on tap, till now I have created the list of checkboxes, but when I tap on one of them, all of them change their state.
I want that only the checkbox I'm tapping should change it's state.
Widget checkbox(){
return InkWell( ///CHECKBOX
onTap: () {
setState(() {
this.value = !this.value;
});
},
child: Container(
decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.white),
child:
value ? Container(
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.green),
child: Icon(
Icons.check,
size: 20.0,
color: Colors.white,
)
)
:
Container(
decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.black, ),
padding: EdgeInsets.all(0.0),
child: Icon(
Icons.circle,
size: 30.0,
color: Colors.white,
),
),
),
);
Edit(s):
I've used Inkwell class for this purpose
'value' is initialized to false
Thanks in advance
put you checkbox in the separate stateful widget so that it can control it's own state
class MyCheckbox extends StatefulWidget {
MyCheckbox({Key key}) : super(key: key);
#override
_MyCheckboxState createState() => _MyCheckboxState();
}
class _MyCheckboxState extends State<MyCheckbox> {
bool value = false;
#override
Widget build(BuildContext context) {
return InkWell(
///CHECKBOX
onTap: () {
setState(() {
this.value = !this.value;
});
},
child: Container(
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.white),
child: value
? Container(
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.green),
child: Icon(
Icons.check,
size: 20.0,
color: Colors.white,
))
: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black,
),
padding: EdgeInsets.all(0.0),
child: Icon(
Icons.circle,
size: 30.0,
color: Colors.white,
),
),
));
}
}
and when you call your widget add key: UniqueKey(), to make sure everything works as expected
Related
For some reason, when I press the TextField, it focuses for a split second and then unfocuses immediately as the soft keyboard comes up. I can still type and submit, but the labelText doesn't disappear like it's supposed to and most importantly, FocusManager.instance.primaryFocus?.unfocus() doesn't let the keyboard disappear.
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(5),
child: Row(
children: [
Expanded(
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100),
),
child: Container(
margin: const EdgeInsets.only(left: 10, bottom: 10, top: 10),
child: TextField(
onTap: () => myFocusNode.requestFocus(),
decoration: const InputDecoration(
labelText: ' Enter task',
border: InputBorder.none,
floatingLabelBehavior: FloatingLabelBehavior.never,
),
controller: textController,
focusNode: myFocusNode,
onSubmitted: (_) {
submit();
myFocusNode.requestFocus();
textController.clear();
},
),
),
),
),
CircleAvatar(
child: TextButton(
onPressed: () {
submit();
myFocusNode.requestFocus();
textController.clear();
},
child: const FittedBox(
child: Text(
'Add',
style: TextStyle(
color: Colors.white,
),
),
),
),
),
],
),
);
}
I think this may be because the app is rebuilt when the soft keyboard shows up, but I'm not sure. What can I do to fix this?
I think you need to remove this:
onTap: () => myFocusNode.requestFocus(),
If you need, you can control the focus action when filed si submitted with the textInputAction property:
// Go to next field
textInputAction: TextInputAction.next
// Go to previous field
textInputAction: TextInputAction.previous
// Don't move focus
textInputAction: TextInputAction.none
// Many other possible values, check the doc ...
UPDATE
When i try your build code on a MediaPad tablet, it work like a charm, here is my implementation:
import 'package:flutter/material.dart';
class DoorMeasure extends StatefulWidget {
const DoorMeasure({Key? key}) : super(key: key);
#override
State<DoorMeasure> createState() => _DoorMeasureState();
}
class _DoorMeasureState extends State<DoorMeasure> {
var myFocusNode;
var textController = TextEditingController()..text = '';
#override
void initState() {
myFocusNode = new FocusNode();
}
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(5),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100),
),
child: Container(
margin: const EdgeInsets.only(left: 10, bottom: 10, top: 10),
child: TextField(
onTap: () => myFocusNode.requestFocus(),
decoration: const InputDecoration(
labelText: ' Enter task',
border: InputBorder.none,
floatingLabelBehavior: FloatingLabelBehavior.never,
),
controller: textController,
focusNode: myFocusNode,
onSubmitted: (_) {
myFocusNode.requestFocus();
textController.clear();
},
),
),
),
),
CircleAvatar(
child: TextButton(
onPressed: () {
myFocusNode.requestFocus();
textController.clear();
},
child: const FittedBox(
child: Text(
'Add',
style: TextStyle(
color: Colors.white,
),
),
),
),
),
],
),
);
}
}
I have tried a lot of packages like Animated Size And Fade and a widget like AnimatedSwitcher and nothing really worked, they just change the widget without animation .. here is my code :
AnimatedSwitcher(
duration: Duration(milliseconds: 1),
child: isCompany
? Container(
key: ValueKey<int>(0),
child: Padding(
padding: const EdgeInsets.fromLTRB(40, 0, 40, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextField(
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Colors.white),
),
prefixIcon: Image.asset(
'assets/images/user_icon.png'),
labelText: 'إسم المستخدم',
labelStyle: TextStyle(
color: Colors.white, fontSize: 18)),
),
TextField(
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Colors.white),
),
prefixIcon: Image.asset(
'assets/images/password_icon.png'),
suffixIcon: IconButton(
icon: Image.asset(
'assets/images/show_icon.png'),
onPressed: () {},
),
labelText: 'كلمة المرور',
labelStyle: TextStyle(
color: Colors.white, fontSize: 18)),
),
],
),
),
)
: Container(
key: ValueKey<int>(1),
child: Padding(
padding: const EdgeInsets.fromLTRB(40, 40, 40, 40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextField(
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Colors.white),
),
prefixIcon: Image.asset(
'assets/images/user_icon.png'),
labelText: 'رقم الجوال',
labelStyle: TextStyle(
color: Colors.white, fontSize: 18)),
),
TextField(
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Colors.white),
),
prefixIcon: Image.asset(
'assets/images/password_icon.png'),
suffixIcon: IconButton(
icon: Image.asset(
'assets/images/show_icon.png'),
onPressed: () {},
),
labelText: 'كلمة المرور',
labelStyle: TextStyle(
color: Colors.white, fontSize: 18)),
),
],
),
),
)
),
This is just changing widget with a click, no animation at all, and I need it to be like this video :
https://youtu.be/Uwe8hHkfd8I
AnimatedSwitcher just switches betwween the widget it displays.
One idea I had thinking about your problem is using a stack with two Positioned Items as your Buttons and one PositionedTransition, that translates between the position of your first and second Positioned Widgets when pressed.
Have a look at this example: https://api.flutter.dev/flutter/widgets/PositionedTransition-class.html
Now I had time to do a quick and easy example of my idea that looks like this:
(It does not lag - this is caused by the conversion of video to gif)
Code that you can copy paste as basis:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
AnimationController _controller;
final double containerWidth = 150.0;
final double containerHeight = 50.0;
final double padding = 20.0;
final double borderWidth = 5.0;
#override
void initState() {
super.initState();
_controller =
AnimationController(duration: Duration(seconds: 1), vsync: this);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: LayoutBuilder(builder: (context, constraints) {
final Size biggest = constraints.biggest;
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.white,
child: Stack(
children: [
PositionedTransition(
rect: RelativeRectTween(
begin: RelativeRect.fromSize(
Rect.fromLTWH(
padding, padding, containerWidth, containerHeight),
biggest),
end: RelativeRect.fromSize(
Rect.fromLTWH(biggest.width - containerWidth - padding,
padding, containerWidth, containerHeight),
biggest),
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
)),
child: Container(
width: containerWidth,
height: containerHeight,
decoration:
BoxDecoration(border: Border.all(width: borderWidth)),
),
),
Positioned(
top: padding + borderWidth,
left: padding + borderWidth,
child: GestureDetector(
onTap: () {
_controller.reverse();
},
child: Container(
width: containerWidth - 2 * borderWidth,
height: containerHeight - 2 * borderWidth,
child: Center(
child: Text(
"Text1",
style: TextStyle(color: Colors.black, fontSize: 20),
),
),
),
)),
Positioned(
top: padding + borderWidth,
left: biggest.width - containerWidth - padding + borderWidth,
child: GestureDetector(
onTap: () {
_controller.forward();
},
child: Container(
width: containerWidth - 2 * borderWidth,
height: containerHeight - 2 * borderWidth,
child: Center(
child: Text(
"Text2",
style: TextStyle(color: Colors.black, fontSize: 20),
),
),
),
)),
],
),
);
}),
);
}
}
Future work:
To achieve what you want in the video you could use the AnimatedSwitcher you already tried to just alternate between the two Input fields depending on the button press.
is there a way to make sure that a widget keeps underneath the quick menu on android.
At the moment I do it in a kind of dirty way with a padding parameter, I hope there is a better solution.
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 22.0),
color: Color(0xff757575),
child: Container(
padding: EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
buildHeader(),
Padding(
padding: const EdgeInsets.only(left: 40.0, right: 40.0),
child: TextField(
// cursorColor: Colors.red,
maxLines: null,
textAlign: TextAlign.left,
autofocus: false,
style: TextStyle(
fontSize: 20.0,
),
decoration: InputDecoration(
hintText: "Add title",
border: InputBorder.none,
),
),
),
Divider(
color: Colors.black,
),
SizedBox(
height: 20.0,
),
TimeAndDateCard(),
EventEntryCard(
label: 'Add Location',
icon: Icon(Icons.place),
onLongPress: () {
showSearch(delegate: LocationSearch(), context: context);
},
),
EventEntryCard(
icon: Icon(Icons.people),
label: 'Invite people ',
),
EventEntryCard(
icon: Icon(Icons.attachment),
label: 'Add attachment',
),
EventEntryCard(
icon: Icon(Icons.work),
label: 'Status',
),
],
),
),
);
}
}
layout without padding
Wished layout. Container is underneath the statusbar
Wrap your Container with SafeArea and it will do the magic for you..
Widget build(BuildContext context) {
return SafeArea(
child: Container(),
);
}
Hope it answers your question..
I have problems using the Navigation in Flutter with the MVVM pattern.
I have two Views:
Login
Dashboard
I Navigate with Navigator.pushReplacementNamed(context, Dashboard) from Login to Dashboard.
When the user loggs out I naviagte from Dashboard to Login with Navigator.pushReplacementNamed(context, Login)
But the i get the following error:
A Login ViewModel was used after being disposed. Once you have called dispose() on a LoginViewModel, it can no longer be used.
I thought that a View\Widget is created when i call it again after it was disposed?
What should i do? It makes no sense to Navigate with
pushNamed
and leave the widget in the widget tree.
My LoginView:
import 'package:app/common/routing_contstants.dart';
import 'package:app/core/viewmodels/views/login_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_signin_button/button_view.dart';
import 'package:flutter_signin_button/flutter_signin_button.dart';
import 'package:app/common/colors.dart';
import 'base_view.dart';
class LoginView extends StatefulWidget {
#override
_LoginView createState() => _LoginView();
}
class _LoginView extends State<LoginView> {
final TextEditingController _mailCtr = TextEditingController();
final TextEditingController _passCtr = TextEditingController();
#override
Widget build(BuildContext context) {
return BaseView<LoginViewModel>(
builder: (context, model, child) => Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
// Add box decoration
decoration: BoxDecoration(
// Box decoration takes a gradient
gradient: LinearGradient(
// Where the linear gradient begins and ends
begin: Alignment.topRight,
end: Alignment.bottomCenter,
// Add one stop for each color. Stops should increase from 0 to 1
stops: [0.2, 0.4, 0.6, 1.0],
colors: [
Colors.deepPurple[800],
Colors.deepPurple[700],
Colors.deepPurple[500],
Theme.of(context).colorScheme.pink,
],
),
),
child: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: <Widget>[
SizedBox(
height: 120,
),
Image.asset(
"assets/images/icon.png",
color: Colors.white,
width: 200,
height: 100,
),
SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _mailCtr,
decoration: InputDecoration(
hintText: "Email",
hintStyle: TextStyle(color: Colors.grey[500]),
icon: Icon(
Icons.email,
color: Colors.white,
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
),
cursorColor: Colors.white,
style: TextStyle(color: Colors.white),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _passCtr,
decoration: InputDecoration(
hintText: "Password",
hintStyle: TextStyle(color: Colors.grey[500]),
icon: Icon(
Icons.lock,
color: Colors.white,
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
),
cursorColor: Colors.white,
style: TextStyle(color: Colors.white),
obscureText: true,
),
),
GestureDetector(
onTap: () {},
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: new Text("Forget Password?",
style: Theme.of(context)
.textTheme
.body1
.copyWith(color: Colors.white)),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: SizedBox(
width: double.infinity,
child: OutlineButton(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: new Text("Login",
style: Theme.of(context)
.textTheme
.title
.copyWith(color: Colors.white))),
onPressed: () async {
var loginSuccess = await model.signInWithEmailAndPassword(context, _mailCtr.text, _passCtr.text);
if (loginSuccess != null) {
Navigator.pushReplacementNamed(context, DashboardRoute);
}
},
// color: Colors.blue,
textColor: Colors.white,
borderSide: BorderSide(color: Colors.white),
)),
),
Padding(
padding: const EdgeInsets.all(0.8),
child: SignInButton(
Buttons.Google,
onPressed: () {},
),
),
Padding(
padding: const EdgeInsets.all(0.8),
child: SignInButton(
Buttons.Facebook,
onPressed: () {},
),
),
SizedBox(
height: 100,
),
],
),
),
)),
),
),
);
}
}
My DashboardView:
import 'package:app/common/routing_contstants.dart';
import 'package:app/core/viewmodels/views/dashboard_view_model.dart';
import 'package:flutter/material.dart';
import 'package:app/common/colors.dart';
import 'base_view.dart';
class DashboardView extends StatefulWidget {
#override
_DashboardView createState() => _DashboardView();
}
class _DashboardView extends State<DashboardView> {
#override
Widget build(BuildContext context) {
return BaseView<DashboardViewModel>(
builder: (context, model, child) => Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
// Add box decoration
decoration: BoxDecoration(
// Box decoration takes a gradient
gradient: LinearGradient(
// Where the linear gradient begins and ends
begin: Alignment.topRight,
end: Alignment.bottomCenter,
// Add one stop for each color. Stops should increase from 0 to 1
stops: [0.3, 0.7, 0.4, 0.3],
colors: [
Theme.of(context).colorScheme.blue,
Theme.of(context).colorScheme.darkBlue,
Theme.of(context).colorScheme.pink,
Colors.pink[600],
],
),
),
child: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: <Widget>[
SizedBox(height: 32.0),
SizedBox(
height: 50.0,
child: _signOutButton(context, model),
),
SizedBox(height: 30.0),
SizedBox(
height: 100,
),
],
),
),
),
),
),
),
);
}
Widget _signOutButton(context, DashboardViewModel model) {
return OutlineButton(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: new Text("Logout",
style: Theme.of(context)
.textTheme
.title
.copyWith(color: Colors.white))),
onPressed: () async {
await model.signOut(context).then(
(_) => {
Navigator.pushReplacementNamed(context, LoginRoute)
},
);
},
// color: Colors.blue,
textColor: Colors.white,
borderSide: BorderSide(color: Colors.white),
);
}
}
My BaseView:
import 'package:app/common/locator.dart';
import 'package:app/core/viewmodels/base_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class BaseView<T extends BaseModel> extends StatefulWidget {
final Widget Function(BuildContext context, T model, Widget child) builder;
final Function(T) onModelReady;
BaseView({this.builder, this.onModelReady});
#override
_BaseViewState<T> createState() => _BaseViewState<T>();
}
class _BaseViewState<T extends BaseModel> extends State<BaseView<T>> {
T model = getIt<T>();
#override
void initState() {
if (widget.onModelReady != null) {
widget.onModelReady(model);
}
super.initState();
}
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>(
create: (context) => model,
child: Consumer<T>(builder: widget.builder));
}
}
User Factory instead of RegisterLazySingleton.
I'm trying to get the splash to match the same shape as my Container that has a FlatButton as its child.
When pressed, the splash currently fills a different shape as shown here:
My code for the widget is below:
import 'package:flutter/material.dart';
class RoundedButton extends StatelessWidget {
const RoundedButton( {this.buttonColor, this.buttonTitle, #required this.onPressed});
final Color buttonColor;
final String buttonTitle;
final Function onPressed;
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 16.0),
height: 42.0,
width: 200.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
color: buttonColor,
),
child: FlatButton(
onPressed: onPressed,
child: Text(
buttonTitle,
style: TextStyle(
color: Colors.white
),
),
),
);
}
}
You can use ClipRRect widget which clips the underlying widget with rounded corners and by using borderRadius property and passing same radius as of parent widget ie, Container, ie, wrap the FlatButton with ClipRRect to achieve desired effect. Sample working code below:
body: Container(
margin: EdgeInsets.symmetric(vertical: 16.0),
height: 42.0,
width: 200.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
color: Colors.red,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(30),
child: FlatButton(
onPressed: () {
print('pressed');
},
child: Text(
'Send',
style: TextStyle(
color: Colors.white
),
),
),
)
),
Hope this answers your question.