Flutter Animated Container - flutter

I have a RaisedButton widget and an AnimatedContainer widget in a screen, and the idea is that upon pressing the RaisedButton the width of the AnimatedContainer would then decrease in a given duration. The documentation of the AnimatedContainer states that all I would need to do is declare the width of the widget as a variable, and then setState(() {}) after changing the value and it will automatically change to that value during the duration. I have tried to implement this and upon pressing the RaisedButton the variables value definitely changes (based on printing the value of it after pressing it), however the widget's width does not change with it. Am I missing something obvious?
My Widgets are within a container in a PageView and my code for the RaisedButton and AnimatedContainer is as follows:
RaisedButton (
onPressed: () {
setState(() {
loginWidth = 70.0;
});
},
),
AnimatedContainer (
duration: new Duration (seconds: 2),
width: loginWidth,
height: 40,
color: Colors.red,
)
Here is my widget tree:
pages.add(
Container(
color: chSecondary,
child: Stack(
children: <Widget>[
Container (
child: Align (
child: Image(image: AssetImage("graphics/signin.png")),
alignment: Alignment.bottomCenter,
),
),
Form(
key: _formKey,
child: new Container(
padding: EdgeInsetsDirectional.only(top: 100, start: 15, end: 15, bottom: 15),
child: new Column(
children: <Widget>[
Container (
child: Image(image: AssetImage("graphics/login.png"), height: 200, width: 200,),
margin: EdgeInsetsDirectional.only(bottom: 20),
),
Container (
padding: EdgeInsets.all(25.0),
decoration: new BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.white,
),
child: Column (
children: <Widget>[
Align(
child: new Text("Email:", style: TextStyle(fontSize: tonSubTitle, color: Colors.black)),
alignment: Alignment.centerLeft,
),
new Container(
child: new TextFormField(
keyboardType: TextInputType.emailAddress,
controller: _email,
style: TextStyle(fontSize: tonText, color: Colors.black),
decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: new BorderRadius.circular(tonRadius)),
contentPadding: EdgeInsetsDirectional.only(top: 15, start: 7.5),
focusedBorder: OutlineInputBorder(borderSide: new BorderSide(color: Colors.grey)),
hintText: "Email Address",
hintStyle: TextStyle(color: Colors.black),
),
validator: (value) {
if (value.isEmpty) {
return "Please enter an email";
}
if (!value.contains("#tonbridge-school.org")) {
return "Please enter a valid email address";
}
},
),
padding: const EdgeInsets.only(top: 10, bottom: 10)
),
Align (
child: new Text("Password:", style: TextStyle(fontSize: tonSubTitle, color: Colors.black)),
alignment: Alignment.centerLeft,
),
new Container(
child: new TextFormField(
obscureText: true,
controller: _password,
style: TextStyle(color: Colors.black, fontSize: tonText),
decoration: InputDecoration(
contentPadding: EdgeInsetsDirectional.only(top: 15, start: 7.5),
border: OutlineInputBorder(borderRadius: new BorderRadius.circular(tonRadius)),
focusedBorder: OutlineInputBorder(borderSide: new BorderSide(color: Colors.grey)),
hintText: "Password",
hintStyle: TextStyle(color: Colors.black),
),
validator: (value) {
if (value.isEmpty) {
return "Please enter a password";
}
},
),
padding: const EdgeInsets.only(top: 10, bottom: 10)
),
RaisedButton (
onPressed: () {
setState(() {
loginWidth = 70.0;
});
},
),
AnimatedContainer (
duration: new Duration (seconds: 2),
width: loginWidth,
height: 40,
color: Colors.red,
)
],
),
)
],
),
)
),
],
),
),
);

The code snippet you've posted is already correct.
Make sure that:
loginWidth is initialized
the new loginWidth value is actually different from the default value
I've copied it and built a minimal example so you can double check the rest of your code. This example also include a surrounding PageView:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyBody(),
),
);
}
}
class MyBody extends StatefulWidget {
#override
_MyBodyState createState() => _MyBodyState();
}
class _MyBodyState extends State<MyBody> {
double loginWidth = 40.0;
#override
Widget build(BuildContext context) {
return Center(
child: PageView(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
RaisedButton (
child: Text('Animate!'),
onPressed: () {
setState(() {
loginWidth = 250.0;
});
},
),
AnimatedContainer (
duration: Duration (seconds: 1),
width: loginWidth,
height: 40,
color: Colors.red,
),
],
)
],
),
);
}
}

do you initialised loginWidth ?
var loginWidth =0.0;
Curve _curve = Curves.fastOutSlowIn;
_doanimation(){
setState(() {
loginWidth ==0.0? loginWidth =100: loginWidth =0.0;
});
}
change function with your case
Column(
children: <Widget>[
Text("welcome"),
RaisedButton(
onPressed: (){
_doanimation();
},
),
AnimatedContainer(
curve: _curve,
duration: Duration(seconds: 1),
width: loginWidth,
height:100
)
],
),

Where do you have loginWidth declared? It needs to outside the scope of the builder function or its value will get reinitialized on every build.
Can you update your example to show where you declare?
Correct:
class _WidgetState extends State<Widget> {
double loginWidth = 0;
#override
Widget build(BuildContext context) {
// return the new widget tree
}
}
Incorrect:
class _WidgetState extends State<Widget> {
#override
Widget build(BuildContext context) {
double loginWidth = 0;
//return the new widget tree
}
}

Related

Keyboard overlay textfield, not working focusNode in Flutter

I have a text field on the page which is located at the very bottom. resizeToAvoidBottomInset value: false. I added a focuseNode so that when the field is clicked, it will be in focus. But when I click on this text field, the keyboard overlaps and nothing is visible because the text field is at the very bottom and the page does not scroll when the keyboard opens. How can I make the text field visible when opening the keyboard? If I use resizeToAvoidBottomInset: true, then the page scrolls, but between the keyboard and the widget there is a large padding that is set at the bottom of the page and I cannot remove this padding.
main page
const Scaffold(
resizeToAvoidBottomInset: false,
body: FormPage(),
),
body
class FormPage extends StatefulWidget {
final int? paid;
final bool? init;
final Function(bool) parking;
final Function(int) valueChange;
const PaidParking({
Key? key,
required this.parking,
required this.paid,
required this.init,
required this.valueChange,
}) : super(key: key);
#override
State<FormPage > createState() => _FormPageState();
}
class _FormPageState extends State<FormPage > {
FocusNode myFocusNodeName = FocusNode();
final TextEditingController controller =
TextEditingController(text: '€59 per h');
#override
void initState() {
setState(() {
paidParking = widget.parkingInit ?? false;
controller.text =
'€${widget.paidInit != null ? (widget.paidInit! / 100) : 2} per h';
});
super.initState();
}
#override
void dispose() {
super.dispose();
myFocusNodeName.dispose();
}
#override
Widget build(BuildContext context) {
myFocusNodeName.addListener(() {
setState(() {});
});
return Padding(
padding: const EdgeInsets.only(top: 15),
child: Column(
children: [
Row(
children: [
Opacity(
opacity: paidParking ? 1 : 0.7,
child: const Text(
'Form Page',
style: TextStyle(
fontSize: 14,
fontFamily: constants.FontFamily.AvenirLtStd,
fontWeight: FontWeight.w700,
),
),
),
const SizedBox(width: 8),
switcher,
],
),
const SizedBox(height: 15),
paidParking ? parkingValueGet() : const SizedBox(),
],
),
);
}
void changeValue(bool operation, {bool editing = false}) {
final String value = controller.text;
double num = double.parse(
value
.replaceAll('€', '')
.replaceAll('per', '')
.replaceAll('h', '')
.replaceAll(' ', ''),
);
if (!editing) {
if (num <= 0 && !operation) return;
operation ? num += 0.05 : num -= 0.05;
controller.text = '€${num.toStringAsFixed(2)} per h';
widget.paidValueChange((num * 100).round());
} else {
num = double.parse(controller.text);
controller.text = '€${num.toStringAsFixed(2)} per h';
num = double.parse(
value
.replaceAll('€', '')
.replaceAll('per', '')
.replaceAll('h', '')
.replaceAll(' ', ''),
);
widget.valueChange((num * 100).round());
}
}
Widget parkingValueGet() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
InkWell(
onTap: () => changeValue(false),
child: _circleBox(false),
),
Column(
children: [
SizedBox(
height: 40,
width: 120,
child: TextField(
controller: controller,
focusNode: myFocusNodeName,
style: const TextStyle(
fontSize: 16,
fontFamily: FontFamily.AvenirBook,
color: constants.Colors.white,
decoration: TextDecoration.none,
),
onSubmitted: (value) => changeValue(true, editing: true),
onEditingComplete: () => changeValue(true, editing: true),
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
signed: true,
),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r"[0-9.]")),
],
textAlign: TextAlign.center,
decoration: const InputDecoration(
border: InputBorder.none,
),
),
),
Container(color: Colors.white, height: 1, width: 115),
],
),
InkWell(
onTap: () => changeValue(true),
child: _circleBox(true),
),
],
);
}
Widget _circleBox(bool operation) {
return Container(
height: 23,
width: 23,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: constants.Colors.white,
),
),
child: Text(
operation ? '+' : '-',
style: constants.Styles.smallBoldTextStyleWhite,
),
);
}
Widget get switcher => Opacity(
opacity: paidParking ? 1 : 0.7,
child: GestureDetector(
onTap: () {
setState(() {
paidParking = !paidParking;
});
widget.paidParking(paidParking);
},
child: Container(
height: 20,
alignment:
paidParking ? Alignment.centerRight : Alignment.centerLeft,
width: 40,
decoration: BoxDecoration(
color: paidParking ? constants.Colors.purpleMain : Colors.white,
borderRadius: BorderRadius.circular(14.5),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Container(
height: 17,
width: 17,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: paidParking ? Colors.white : const Color(0xff484452),
),
),
),
),
),
);
}
First I can recommend you to do some changes.
Remove setState() method calling inside initState()(have no
effect).
Remove totally the part of the below code. When you are using TexField
inside a StatefulWidget, every time the keyboard opened it will
recall your build() method and in this case, will add multiple focus
listeners on each build() method call, also each focus listener is
calling the setState() method which is triggering build() method
again. This is kind of an infinity loop. I wonder when you open the
keyboard, does your app stuck or stop working smoothly?
myFocusNodeName.addListener(() {
setState(() {});
});

my provider is not found, when I use selector

I run the below code using consumer and provider and it worked smoothly but when I add selector it geive me an error,
the error:
Error: Could not find the correct Provider above this Selector<SignInUpProvider, int> Widget
This happens because you used a BuildContext that does not include the provider
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';
import 'providers/sign_in_up_provider.dart';
import 'utilities/myColors.dart' ;
class SigInUpPage extends StatefulWidget {
const SigInUpPage({Key? key}) : super(key: key);
#override
_SigInUpPageState createState() => _SigInUpPageState();
}
class _SigInUpPageState extends State<SigInUpPage> {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(create: (context)=> SignInUpProvider(),
child: const SignInUpScreen(),);
}
}
class SignInUpScreen extends StatefulWidget {
const SignInUpScreen({Key? key}) : super(key: key);
#override
_SignInUpScreenState createState() => _SignInUpScreenState();
}
class _SignInUpScreenState extends State<SignInUpScreen> {
// determine the sign in or sign up screen!!
// 0 = sign up , 1 = sign in
// sign up contents
// final GlobalKey<FormState> _keySignUp = GlobalKey<FormState>();
final TextEditingController _nameSignUp = TextEditingController();
final TextEditingController _passSignup = TextEditingController();
final TextEditingController _emailSignUp = TextEditingController();
// sign in contents
final GlobalKey<FormState> _keySignIn = GlobalKey<FormState>();
final TextEditingController _emailSignIn = TextEditingController();
final TextEditingController _passSifnIn = TextEditingController();
#override
void dispose() {
_nameSignUp.dispose();
_passSifnIn.dispose();
_passSignup.dispose();
_emailSignIn.dispose();
_emailSignUp.dispose();
// TODO: implement dispose
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.white,
child: Stack(
children: [
// dummy design
firstScreen(context),
// dummy design
secondScreen(context),
// sign in screen
thirdScreen(context),
],
),
),
);
}
// first background in stack
Widget firstScreen(BuildContext context){
return
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color:MyColors.blueColor(),
);
}
// second background in stack
Widget secondScreen(BuildContext context){
return Align(
alignment: Alignment.bottomRight,
child: Container(
decoration: BoxDecoration(
color: MyColors.purple(),
borderRadius: const BorderRadius.only(bottomLeft:Radius.circular(1000),
topLeft: Radius.circular(1000)
)
),
width: MediaQuery.of(context).size.width * 0.5,
height: MediaQuery.of(context).size.height,
),
);
}
// sign in up screen // the white box
Widget thirdScreen(BuildContext context){
return Align(alignment: Alignment.bottomCenter,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
margin: EdgeInsets.only(bottom: MediaQuery.of(context).size.height*0.1),
width: MediaQuery.of(context).size.width*0.7,
height: MediaQuery.of(context).size.height*0.7,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(50))),
child: Selector<SignInUpProvider, int>(
selector: (context, userState) => userState.getUserState,
builder: (context, state, widget){
return Form(
key: _keySignIn,
child: (state == 0 )?
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:
[
logo(context),
nameBox(context),
emailBox(context),
passText(context),
signButton(context),
// this to change between sign in and sign up
changeState(context),
],
):
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:
[
logo(context),
emailBox(context),
passText(context),
signButton(context),
// this to change between sign in and sign up
changeState(context),
],
),
);
}
),
),
),
);
}
// the logo inside the white box
Widget logo(context) {
return Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(50))
),
width: MediaQuery.of(context).size.width*0.7,
height: MediaQuery.of(context).size.height*0.1,
child: const Align(
alignment: Alignment.center,
child: Text("mekyajat",style: TextStyle(
fontStyle: FontStyle.italic,
fontSize: 16,
color: Color.fromRGBO(249, 229, 230, 1),
),)),
);
}
// name box
Widget nameBox(context){
return Container(
width: MediaQuery.of(context).size.width*0.55,
height: MediaQuery.of(context).size.height*0.1,
decoration: const BoxDecoration(
color: Colors.white70,
),
child: TextFormField(
controller:_nameSignUp ,
decoration: const InputDecoration(
icon: Icon(Icons.person),
hintText: "name",
hintStyle: TextStyle(
color: Colors.black12,
fontSize: 14
),
),
textAlign: TextAlign.center,
validator: (value){
if( value.toString().length < 8 ){
Fluttertoast.showToast(msg: "please insert your name \n more than 8 charecters",
gravity: ToastGravity.CENTER,
toastLength: Toast.LENGTH_SHORT,
backgroundColor: Colors.redAccent,
textColor: Colors.white,
fontSize: 16
);
} return null ;
},
),
);
}
Widget emailBox(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width*0.55,
height: MediaQuery.of(context).size.height*0.1,
decoration: const BoxDecoration(
color: Colors.white70,
),
child: TextFormField(
controller:(context.read<SignInUpProvider>().getUserState
== 0) ? _emailSignUp : _emailSignIn ,
decoration: const InputDecoration(
icon: Icon(Icons.email_outlined),
hintText: "E-mail #",
hintStyle: TextStyle(
color: Colors.black12,
fontSize: 14
),
),
textAlign: TextAlign.center,
validator: (value){
if( value.toString().length < 10 ){
Fluttertoast.showToast(msg: "please insert your email \n ",
gravity: ToastGravity.CENTER,
toastLength: Toast.LENGTH_SHORT,
backgroundColor: Colors.redAccent,
textColor: Colors.white,
fontSize: 16
);
}
else if (!containSymbol(value.toString(), "#")){
return "please insert # symbol";
}
return null ;
},
),
);
}
Widget passText(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width*0.55,
height: MediaQuery.of(context).size.height*0.1,
decoration: const BoxDecoration(
color: Colors.white70,
),
child: TextFormField(
controller:(context.read<SignInUpProvider>().getUserState ==0)? _passSignup :_passSifnIn ,
decoration: const InputDecoration(
icon: Icon(Icons.password_sharp),
hintText: "password",
hintStyle: TextStyle(
color: Colors.black12,
fontSize: 14
),
),
textAlign: TextAlign.center,
validator: (value){
if( value.toString().length < 8 ){
Fluttertoast.showToast(msg: "please insert your password \n more than 8 charecters",
gravity: ToastGravity.CENTER,
toastLength: Toast.LENGTH_SHORT,
backgroundColor: Colors.redAccent,
textColor: Colors.white,
fontSize: 16
);
} return null ;
},
),
);
}
Widget signButton(BuildContext context) {
return Selector<SignInUpProvider, int>(
selector: (context,userState)=> userState.getUserState,
builder: (context, currentState, widget){
return InkWell(
child: Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width*0.40,
height: MediaQuery.of(context).size.height*0.06,
child: (currentState == 0 )?
const Text("SIGN UP", textAlign: TextAlign.center,style: TextStyle(color: Colors.white),):
const Text("SIGN IN",textAlign: TextAlign.center, style: TextStyle(color: Colors.white),),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(25)),
gradient: LinearGradient(colors: [
MyColors.blueColor(),
MyColors.purple(),
]),
),
),
onTap: (){},
);
},
);
}
Widget changeState(BuildContext context) {
return Consumer<SignInUpProvider>(
builder: (context, userSt , widget){
return InkWell(
child: SizedBox(
width: MediaQuery.of(context).size.width*0.55,
height: MediaQuery.of(context).size.height*0.05,
child:(userSt.userState == 0)?
const Text("sign in",
textAlign: TextAlign.right,
style: TextStyle(color:Color.fromRGBO(205, 221, 232, 1),fontSize: 12,
),) :
const Text("sign up",
textAlign: TextAlign.right,
style: TextStyle(
color:Color.fromRGBO(205, 221, 232, 1),fontSize: 12,
),)
),
onTap: (){
userSt.changeUserState(userSt.userState);
// SignInUpProvider().changeUserState(userState);
// signProvider.changeUserState(signProvider.userState);
},
);
},
);
}
}
// provide this function with one char and one string
// this run to see if the string contains char or not
bool containSymbol(String fullString , String char){
bool _result = false ;
List<String> _chars = [];
int _stringLingth = fullString.length;
int _keyLoop = 0 ;
while (_keyLoop < _stringLingth){
_chars.add(fullString[_keyLoop]);
_keyLoop++;
}
for(int x = 0 ; x < _chars.length; x++){
if(_chars[x] == char){
_result = true;
break;
}
}
return _result;
}
1 - move up your ChangeNotifierProvider step above in the widget tree for example in the routes you can wrap the SigInUpPage like this :
ChangeNotifierProvider<SignInUpProvider>(
create: (_) => SignInUpProvider(),
child: SigInUpPage(),
),
2 - don't pass the context as a parameter to the other screens,use a builder in each screen like this for the second screen:
class secondScreen extends StatelessWidget {
const secondScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.bottomCenter,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
margin: EdgeInsets.only(bottom: MediaQuery.of(context).size.height*0.1),
width: MediaQuery.of(context).size.width*0.7,
height: MediaQuery.of(context).size.height*0.7,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(50))),
child: Selector<SignInUpProvider, int>(
selector: (context, userState) => userState.getUserState,
builder: (context, state, widget){
return Form(
key: _keySignIn,
child: (state == 0 )?
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:
[
logo(context),
nameBox(context),
emailBox(context),
passText(context),
signButton(context),
// this to change between sign in and sign up
changeState(context),
],
):
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:
[
logo(context),
emailBox(context),
passText(context),
signButton(context),
// this to change between sign in and sign up
changeState(context),
],
),
);
}
),
),
),
);
}
}
this should work

Comment Box as Facebook in flutter

Here is my code
TextField(
controller: commentController,
maxLines: 3,
selectionHeightStyle: BoxHeightStyle.tight,
decoration: new InputDecoration(
hintText: 'Write a Comment',
hintStyle: new TextStyle(
color: Colors.grey,
),
prefixIcon: InkWell(
child: Icon(Icons.camera_alt),
onTap: () {
chooseImage();
},
),
suffixIcon: InkWell(
child: Icon(
Icons.send,
),
onTap: () {
if (filePickedName == 'nofile') {
insertMethod();
commentController.clear();
_fleshScreen();
getCommentData();
} else {
upload();
commentController.clear();
_fleshScreen();
getCommentData();
}
},
)),
style: new TextStyle(
color: Colors.black,
),
),
I want to create as this box with ImageView
facebook comment multimedia box
let me give you a widget for that
class CommentBox extends StatefulWidget {
final Widget image;
final TextEditingController controller;
final BorderRadius inputRadius;
final Function onSend,onImageRemoved;
const CommentBox({Key key, this.image, this.controller, this.inputRadius, this.onSend , this.onImageRemoved }) : super(key: key);
#override
_CommentBoxState createState() => _CommentBoxState();
}
class _CommentBoxState extends State<CommentBox> {
Widget image;
#override
void initState() {
image = widget.image;
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Divider(
height: 1,
color: Colors.grey[300],
thickness: 1,
),
const SizedBox(height: 20),
if (image != null)
_removable(
context,
_imageView(context),
),
if(widget.controller!=null) TextFormField(
controller: widget.controller,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(Icons.send,color: Theme.of(context).primaryColor,),
onPressed: widget.onSend,
),
filled: true,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: widget.inputRadius ?? BorderRadius.circular(32),
),
),
),
],
);
}
Widget _removable(BuildContext context, Widget child) {
return Stack(
alignment: Alignment.topRight,
children: [
child,
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
setState(() {
image = null;
widget.onImageRemoved();
});
},
)
],
);
}
Widget _imageView(BuildContext context) {
return Padding(
padding: EdgeInsets.all(16),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: image,
),
);
}
}
USE IT LIKE ANY WIDGET
MaterialApp(
home: Scaffold(
body: SafeArea(
child: Column(
children: [
SizedBox(height: 128,),
Spacer(),
CommentBox(
image: Image.asset(
"assets/svg/barber.svg",
height: 64,
width: 64,
),
controller: TextEditingController(),
onImageRemoved: (){
//on image removed
},
onSend: (){
//on send button pressed
},
),
],
),
),
),
)

How to get static container background but scrollable loginform?

I want to scroll the loginform but NOT the background, but i noticed that the cointaner that contains these "circles" are moved to the up when keyboards appears, so i added
resizeToAvoidBottomInset: true,
To the scaffold, but now i cant scroll anything in the loginform and thats is what i dont want, i want to scroll ONLY the loggin form..
Here is the screenshot of the emulator, just click this
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:landscapes/bloc/inherited_provider.dart';
import 'package:landscapes/bloc/login_bloc.dart';
import 'package:landscapes/pages/register_page.dart';
class LoginPage extends StatefulWidget {
LoginPage({Key key}) : super(key: key);
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
body: Form(
key: formKey,
child: Stack(
children: <Widget>[
_crearFondo(),
_loginForm(context),
],
),
));
}
Widget _crearFondo() {
Size size = MediaQuery.of(context).size;
final fondoGris = Container(
height: size.height, width: size.width, color: Colors.grey[850]);
final circulo = Container(
width: 90.0,
height: 90.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
color: Color.fromRGBO(255, 255, 255, 0.05)),
);
return Stack(
children: <Widget>[
fondoGris,
Positioned(top: 50.0, left: 30.0, child: circulo),
Positioned(top: 150.0, right: 50.0, child: circulo),
Positioned(bottom: 180.0, right: 20.0, child: circulo),
Positioned(bottom: 280.0, left: 40.0, child: circulo),
],
);
}
Widget _loginForm(context) {
final bloc = InheritedProvider.loginBlocInherit(context);
final size = MediaQuery.of(context).size;
return SingleChildScrollView(
child: Column(
children: <Widget>[
SafeArea(child: Container(height: size.height * 0.05)),
SizedBox(height: size.height * 0.05),
welcomeBackForm(),
SizedBox(height: 55.0),
_formEmail(bloc),
SizedBox(height: 30.0),
_formPassword(),
SizedBox(height: 38),
_button(),
SizedBox(height: 50),
_crearCuenta(),
],
),
);
}
//MENSAJE BIENVENIDA
Widget welcomeBackForm() {
return Container(
alignment: Alignment.topLeft,
margin: EdgeInsets.only(left: 40.0),
child: Text(
'Hello!\nWelcome back',
style: GoogleFonts.playfairDisplay(
fontSize: 30, fontWeight: FontWeight.w600, color: Colors.white),
),
);
}
//TEXTFORMFIELD DEL EMAIL
Widget _formEmail(LoginBloc bloc) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 30.0),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
icon: Icon(
Icons.email,
color: Colors.white,
),
hintText: 'Email adress',
filled: true,
fillColor: Colors.grey[600],
enabledBorder: OutlineInputBorder(borderSide: BorderSide.none),
border: OutlineInputBorder(borderSide: BorderSide.none)),
style: TextStyle(height: 1),
validator: (value) {
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = new RegExp(pattern);
if (regExp.hasMatch(value)) {
return null;
} else {
return ('El email no es correcto');
}
},
),
);
}
//TEXTFORMFIELD DEL PASSWORD
Widget _formPassword() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 30.0),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
icon: Icon(
Icons.lock,
color: Colors.white,
),
hintText: 'Password',
filled: true,
fillColor: Colors.grey[600],
enabledBorder: OutlineInputBorder(borderSide: BorderSide.none),
border: OutlineInputBorder(borderSide: BorderSide.none),
),
style: TextStyle(height: 1),
),
);
}
//BOTON INGRESAR
Widget _button() {
return RaisedButton(
color: Color.fromRGBO(254, 200, 140, 1),
padding: EdgeInsets.symmetric(horizontal: 90.0, vertical: 15.0),
child: Text('ingresar'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
elevation: 0.0,
onPressed: () => _submit(),
);
}
//FLATBUTTON DE CREAR CUENTA
Widget _crearCuenta() {
return FlatButton(
onPressed: () => Navigator.pushReplacementNamed(context, 'registerpage'),
child: Text(
'Crear una nueva cuenta',
style: TextStyle(fontSize: 14),
),
textColor: Colors.white,
);
}
//SUBMIT DEL BOTON
void _submit() async {
if (!formKey.currentState.validate()) return null;
formKey.currentState.save();
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext context) => RegisterPage()));
// Navigator.pushReplacementNamed(context, HomePage.routName).then((value) { setState(() { });});
}
}
You may try wrapping your _loginForm with SingleChildScrollView as follows :
SingleChildScrollView(child: _loginForm(context)),
By doing so the widgets within _crearFondo() will not scroll, however the widgets within _loginForm will scroll.
Please review the Flutter Documentation for SingleChildScrollView
SingleChildScrollView
A box in which a single widget can be scrolled.
This widget is useful when you have a single box that will normally be
entirely visible, for example a clock face in a time picker, but you
need to make sure it can be scrolled if the container gets too small
in one axis (the scroll direction).
It is also useful if you need to shrink-wrap in both axes (the main
scrolling direction as well as the cross axis), as one might see in a
dialog or pop-up menu. In that case, you might pair the
SingleChildScrollView with a ListBody child.
When you have a list of children and do not require cross-axis
shrink-wrapping behavior, for example a scrolling list that is always
the width of the screen, consider ListView, which is vastly more
efficient that a SingleChildScrollView containing a ListBody or Column
with many children.

Flutter stack layout container use remaining height

I'm starting with Flutter.
I'm building a layout to look like
I'm using Stack to build this layout, where the part in background color is another Stack child with parent Container height to a fixed value.
The code I have written till now is
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:whatsappwithoutcontact/components/form_fields.dart';
import 'package:whatsappwithoutcontact/screens/info/info-screen.dart';
class MessageScreen extends StatefulWidget {
#override
_MessageScreenState createState() => _MessageScreenState();
}
class _MessageScreenState extends State<MessageScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('WhatsApp without Contact'),
backgroundColor: Colors.transparent,
elevation: 0.0,
actions: [
IconButton(
icon: Icon(Icons.info),
onPressed: _onInfoPressed,
)
],
),
extendBodyBehindAppBar: true,
body: MessageContainer()
);
}
void _onInfoPressed() {
Navigator.of(context).push(
MaterialPageRoute<void> (
builder: (BuildContext context) {
return InfoScreen();
}
)
);
}
}
class MessageContainer extends StatefulWidget {
#override
_MessageContainerState createState() => _MessageContainerState();
}
class _MessageContainerState extends State<MessageContainer> {
static const double avatarRadius = 35;
static const double titleBottomMargin = (avatarRadius * 2) + 18;
static const double _headerHeight = 350.0;
final _formKey = GlobalKey<FormState>();
final _messageFieldFocusNode = FocusNode();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Stack(
children: [
Container(
height: _headerHeight,
child: Stack(
children: <Widget>[
Container(
child: ClipPath(
clipper: HeaderClipper(avatarRadius: avatarRadius),
child: CustomPaint(
size: Size(MediaQuery.of(context).size.width, _headerHeight),
painter: HeaderPainter(
color: Colors.green,
avatarRadius: avatarRadius
),
),
),
),
Positioned(
left: 0,
right: 0,
child: Container(
padding: EdgeInsets.only(top: 80.0),
margin: EdgeInsets.all(50.0),
child: Column(
children: <Widget>[
FormTextField(
labelText: 'Country Code',
validator: (value) {
if (value.isEmpty) {
return 'Required';
}
return null;
}
),
SizedBox(height: 15.0,),
FormTextField(
labelText: 'Phone Number',
validator: (value) {
if (value.isEmpty) {
return 'Required';
}
return null;
},
)
],
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: CircleAvatar(
radius: avatarRadius,
backgroundColor: Colors.green,
child: IconButton(icon: Icon(Icons.message), onPressed: _onAddMessageButtonClick,),
),
)
],
),
),
SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(
vertical: _headerHeight,
horizontal: 50
),
child: Column(
children: [
TextFormField(
keyboardType: TextInputType.multiline,
maxLines: 4,
focusNode: _messageFieldFocusNode,
decoration: InputDecoration(
labelText: 'Message',
fillColor: Colors.white,
labelStyle: TextStyle(
color: _messageFieldFocusNode.hasFocus ? Colors.green : Colors.grey
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey
)
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.green
)
)
),
)
],
),
),
),
],
)
);
}
}
The first Container inside the first Stack is the green background part and SingleChildScrollView is the part below the green color background.
The SingleChildScrollView is making the lower part scrollable whereas I want it to be fixed and take up remaining space below the message icon in the center.
I tried using Container but then the Message input field is not displayed.
How can I use the remaining space below the Stack layout?
Is my layout structure good according to the design. I need suggestions on how to improve it, if not good.
Replace the first Stack widget with Column, now you can wrap your SingleChildScrollView widget into an Expanded widget, the Expanded widget works only inside Column and Row to fill the available space.
Don't forget to remove vertical padding from the Message input field.