How can I customise the flexibleSpace property in SliverAppBar in Flutter? - dart

Instead of using FlexibleSpaceBar in the flexibleSpace property of SliverAppBar I want to use a custom widget tree, when expanded, but upon scrolling, I want to display a custom text, and not the widget tree.
I have created a custom widget tree which shall be assigned to the flexibleSpace property, but I don't know how to display custom text on scrolling, and hide the widget tree.
SliverAppBar(
expandedHeight: 180.0,
backgroundColor: const Color(0xFF9e0118),
iconTheme: IconThemeData(color: Colors.white),
floating: true,
pinned: true,
flexibleSpace: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 16.0),
padding: EdgeInsets.only(left: 32.0, right: 32.0),
child: Text(
'Some text',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontFamily: 'PlayfairDisplay',
fontStyle: FontStyle.italic,
fontSize: 16.0),
)),
Container(
margin: EdgeInsets.only(top: 16.0),
padding: EdgeInsets.only(left: 32.0, right: 32.0),
child: Text(
'some text',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontFamily: 'PlayfairDisplay',
fontSize: 16.0),
)),
],
),
),

You may want to wrap your widget tree inside a FlexibleSpaceBar widget and add your widget tree as background. I hope I understood your question right. Check this gif.
SliverAppBar(
expandedHeight: 180.0,
backgroundColor: const Color(0xFF9e0118),
iconTheme: IconThemeData(color: Colors.white),
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
centerTitle: true,
background: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 16.0),
padding: EdgeInsets.only(left: 32.0, right: 32.0),
child: Text(
'Some text',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontFamily: 'PlayfairDisplay',
fontStyle: FontStyle.italic,
fontSize: 16.0),
)),
Container(
margin: EdgeInsets.only(top: 16.0),
padding: EdgeInsets.only(left: 32.0, right: 32.0),
child: Text(
'some text',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontFamily: 'PlayfairDisplay',
fontSize: 16.0),
)),
],
),
),
),

To complete #westdabestdb answer, to make the title appear on scroll, it's really simple.
You just need to add the Animated Widget of you your choice and use the addListener from your scroll controller to switch a boolean. To find more Animated Widgets go to the official docs for Flutter Animated Widgets: https://flutter.dev/docs/development/ui/widgets/animation
Here is an example with an AnimatedOpacity Widget and the Parallax collapse mode which in my opinion work pretty well together.
class YourPage extends StatelessWidget {
const YourPage({Key key}) : super(key: key);
ScrollController _scrollController;
bool _isScrolled = false;
#override
void initState() {
_scrollController = ScrollController();
_scrollController.addListener(_listenToScrollChange);
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
expandedHeight: 144.0,
pinned: true,
forceElevated: true,
////////////////////////////////////
// HERE IS THE PART TO BE ADDED
title: AnimatedOpacity(
duration: Duration(milliseconds: 300),
opacity: _isScrolled ? 1.0 : 0.0,
curve: Curves.ease,
child: Text("YOUR TITLE HERE"),
),
////////////////////////////////////
flexibleSpace: FlexibleSpaceBar(
titlePadding: const EdgeInsets.only(bottom: 8.0),
centerTitle: true,
collapseMode: CollapseMode.parallax,
background: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 16.0),
padding: EdgeInsets.only(left: 32.0, right: 32.0),
child: Text(
'Some text',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontFamily: 'PlayfairDisplay',
fontStyle: FontStyle.italic,
fontSize: 16.0),
)),
Container(
margin: EdgeInsets.only(top: 16.0),
padding: EdgeInsets.only(left: 32.0, right: 32.0),
child: Text(
'some text',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontFamily: 'PlayfairDisplay',
fontSize: 16.0),
)),
],
),
),
)
],
),
),
);
}
// METHODS
void _listenToScrollChange() {
if (_scrollController.offset >= 48.0) {
setState(() {
_isScrolled = true;
});
} else {
setState(() {
_isScrolled = false;
});
}
}
}
You will just have to play around with the values of the if in the _listenToScrollChange
and the Duration of the animation to obtain the desired output.

Related

Flutter page is not scrollable: How to scroll when more than one widget?

I am trying to make my page scrollable but I didn't manage it. What I tried so far is to use SingeChildScrollView, ListView and ScrollView...
Everytime I use one of these an error occurs. How can I manage this?
Is there any alternative to use those three options?
I am new to flutter and what I googled so far didn't work because I could only find solutions with one of these three scrolling options.
Thank you!
This is my code:
class LadevorgangScreen extends StatefulWidget {
#override
_LadevorgangScreen createState() => _LadevorgangScreen();
}
#override
class _LadevorgangScreen extends State<LadevorgangScreen> {
//class LadevorgangScreen extends StatelessWidget {
PageController pageController = PageController(initialPage: 0);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Ladevorgang'),
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xffFBD23E), Color(0xffF6BE03)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
),
),
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xffFEFDFD), Color(0xffBDBDB2)],
begin: Alignment.topLeft,
end: Alignment.bottomRight),
),
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(10, 15, 10, 45),
child: FlatButton(
onPressed: () {},
color: Colors.blue,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: 'FAQs:\n',
style: TextStyle(
fontFamily: 'Avenir',
fontSize: 22.0,
fontWeight: FontWeight.w900,
decoration: TextDecoration.underline,
color: Color(0xff232323),
),
children: [
TextSpan(
text: 'Ladevorgang des Elektroautos',
style: TextStyle(
fontFamily: 'Avenir',
fontSize: 20.0,
fontWeight: FontWeight.w500,
decoration: TextDecoration.none,
color: Color(0xff232323)),
),
],
),
),
),
),
Flexible(
child: Text(
'Zu Hause laden',
style: TextStyle(
fontSize: 18.5,
fontFamily: 'Avenir',
fontWeight: FontWeight.w700),
),
),
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Column(
children: [
Row(
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 12, 7, 12),
child: Icon(
Icons.circle_outlined,
size: 15.0,
color: Color(0xffF6BE03),
),
),
Flexible(
child: Text(
"Wie lade ich zu Hause?",
style:
TextStyle(fontSize: 17.0, fontFamily: 'Avenir'),
),
),
],
),
Row(
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 12, 7, 12),
child: Icon(
Icons.circle_outlined,
size: 15.0,
color: Color(0xffF6BE03),
),
),
Flexible(
child: Text(
"Benötige ich überhaupt eine Lademöglichkeit zu Hause?",
style: TextStyle(
fontSize: 17.0, fontFamily: 'Avenir')),
),
],
),
],
),
),
],
),
),
);
}
}
JUST WRAP your main widget OF BODY in SingleChildScrollView widget
body: SingleChildScrollView(
child: Container(
decoration: BoxDecoration
.....
..
.

RenderCustomMultiChildLayoutBox object was given an infinite size during layout error

I keep getting the error as mentioned when I try to load LessonPage. What is happening is that I have a RootPage which checks if a user is signed in, if he is the RootPage will show the LessonPage but if he is not, it will show the LoginScreen which when the user logs in, will invoke a callback function to RootPage _onLoggedIn()so as to switch pages to the LessonPage.
Update: I found out that the error is also logged when I load the app (i.e. the app opens to a LoginScreen), however I see my login screen instead of a blank screen. I've appended my LoginScreen code below for reference
Root Page:
class RootPage extends StatefulWidget {
RootPage({this.auth});
final BaseAuth auth;
#override
State<StatefulWidget> createState() => new _RootPageState();
}
enum AuthStatus {
NOT_DETERMINED,
NOT_LOGGED_IN,
LOGGED_IN,
}
class _RootPageState extends State<RootPage> {
AuthStatus authStatus = AuthStatus.NOT_DETERMINED;
String _userId = "";
#override
void initState() {
super.initState();
widget.auth.getCurrentUser().then((user) {
setState(() {
if (user != null) {
_userId = user?.uid;
}
authStatus =
user?.uid == null ? AuthStatus.NOT_LOGGED_IN : AuthStatus.LOGGED_IN;
});
});
}
void _onLoggedIn() {
widget.auth.getCurrentUser().then((user){
setState(() {
_userId = user.uid.toString();
});
});
setState(() {
authStatus = AuthStatus.LOGGED_IN;
});
}
void _onSignedOut() {
setState(() {
authStatus = AuthStatus.NOT_LOGGED_IN;
_userId = "";
});
}
Widget _buildWaitingScreen() {
return Scaffold(
body: Container(
alignment: Alignment.center,
child:
ColorLoader5(
dotOneColor: Colors.blueGrey[600],
dotTwoColor: Colors.blueGrey[700],
dotThreeColor: Colors.blueGrey[800],
dotType: DotType.circle,
dotIcon: Icon(Icons.adjust),
duration: Duration(seconds: 1),
),
),
);
}
#override
Widget build(BuildContext context) {
switch (authStatus) {
case AuthStatus.NOT_DETERMINED:
return _buildWaitingScreen();
break;
case AuthStatus.NOT_LOGGED_IN:
return new LoginScreen(
auth: widget.auth,
onSignedIn: _onLoggedIn,
);
break;
case AuthStatus.LOGGED_IN:
if (_userId.length > 0 && _userId != null) {
return new Container(child: SingleChildScrollView(child: LessonPage(
title: LESSON_PAGE_TITLE,
userId: _userId,
auth: widget.auth,
onSignedOut: _onSignedOut,
)));
} else return _buildWaitingScreen();
break;
default:
return _buildWaitingScreen();
}
}
}
Lesson Page:
class LessonPage extends StatefulWidget {
LessonPage({Key key, this.auth, this.userId, this.onSignedOut, this.title}) : super(key: key);
final String title;
final BaseAuth auth;
final VoidCallback onSignedOut;
final String userId;
#override
_LessonPageState createState() => _LessonPageState();
}
class _LessonPageState extends State<LessonPage> {
List lessons;
#override
void initState() {
lessons = StaticMethods.getLessons();
super.initState();
}
#override
Widget build(BuildContext context) {
ListTile makeListTile(Lesson lesson) => ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: Container(
padding: EdgeInsets.only(right: 12.0),
decoration: new BoxDecoration(
border: new Border(
right: new BorderSide(width: 1.0, color: Colors.white24))),
child: IconButton(
icon: Icon(Icons.file_download, color: Colors.white),
onPressed: (){},
),
),
title: Text(
lesson.title,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
subtitle: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
child: LinearProgressIndicator(
backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
value: lesson.indicatorValue,
valueColor: AlwaysStoppedAnimation(Colors.green)),
)),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(lesson.level,
style: TextStyle(color: Colors.white))),
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailPage(lesson: lesson)));
},
);
Card makeCard(Lesson lesson) => Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(64, 75, 96, .9)),
child: makeListTile(lesson),
),
);
final makeBody = Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
return makeCard(lessons[index]);
},
),
);
final makeBottom = Container(
height: 55.0,
child: BottomAppBar(
color: Color.fromRGBO(58, 66, 86, 1.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
icon: Icon(Icons.school, color: Colors.white),
onPressed: () => StaticMethods.goToWidget(context, new LessonPage(title: LESSON_PAGE_TITLE)),
),
IconButton(
icon: Icon(Icons.flight_takeoff, color: Colors.white),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.account_box, color: Colors.white),
onPressed: () {},
)
],
),
),
);
final topAppBar = AppBar(
elevation: 0.1,
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
title: Text(widget.title),
automaticallyImplyLeading: false,
);
return Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: topAppBar,
body: makeBody,
bottomNavigationBar: makeBottom,
);
}
}
LoginScreen Containers:
Widget OptionPage() {
return new Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
color: Color.fromRGBO(58, 66, 86, 1.0),
image: DecorationImage(
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.1), BlendMode.dstATop),
image: AssetImage('assets/images/drones.jpg'),
fit: BoxFit.cover,
),
),
child: new Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 250.0),
child: Center(
child: Icon(
Icons.school,
color: Colors.white,
size: 40.0,
),
),
),
Container(
padding: EdgeInsets.only(top: 20.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
LOGIN_SCREEN_TITLE,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
],
),
),
new Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(left: 30.0, right: 30.0, top: 150.0),
alignment: Alignment.center,
child: new Row(
children: <Widget>[
new Expanded(
child: new OutlineButton(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
color: Color.fromRGBO(58, 66, 86, 1.0),
highlightedBorderColor: Colors.white,
onPressed: () => gotoSignup(),
child: new Container(
padding: const EdgeInsets.symmetric(
vertical: 20.0,
horizontal: 20.0,
),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Expanded(
child: Text(
LOGIN_SCREEN_SIGN_UP,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
],
),
),
),
),
],
),
),
new Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(left: 30.0, right: 30.0, top: 30.0),
alignment: Alignment.center,
child: new Row(
children: <Widget>[
new Expanded(
child: new FlatButton(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
color: Colors.white,
onPressed: () => gotoLogin(),
child: new Container(
padding: const EdgeInsets.symmetric(
vertical: 20.0,
horizontal: 20.0,
),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Expanded(
child: Text(
LOGIN_SCREEN_LOGIN,
textAlign: TextAlign.center,
style: TextStyle(
color: Color.fromRGBO(58, 66, 86, 1.0),
fontWeight: FontWeight.bold),
),
),
],
),
),
),
),
],
),
),
],
),
);
}
Widget LoginPage() {
return new Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
color: Colors.white,
image: DecorationImage(
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.05), BlendMode.dstATop),
image: AssetImage('assets/images/drones.jpg'),
fit: BoxFit.cover,
),
),
child: new Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(120.0),
child: Center(
child: Icon(
Icons.school,
color: Color.fromRGBO(58, 66, 86, 1.0),
size: 50.0,
),
),
),
new Row(
children: <Widget>[
new Expanded(
child: new Padding(
padding: const EdgeInsets.only(left: 40.0),
child: new Text(
LOGIN_SCREEN_EMAIL,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color.fromRGBO(58, 66, 86, 1.0),
fontSize: 15.0,
),
),
),
),
],
),
new Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(left: 40.0, right: 40.0, top: 10.0),
alignment: Alignment.center,
padding: const EdgeInsets.only(left: 0.0, right: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Expanded(
child: TextFormField( // LOGIN SCREEN EMAIL
maxLines: 1,
keyboardType: TextInputType.emailAddress,
autofocus: false,
textAlign: TextAlign.left,
decoration: InputDecoration(
icon: Icon(Icons.alternate_email),
hintText: LOGIN_SCREEN_EMAIL_HINT,
hintStyle: TextStyle(color: Colors.grey),
),
validator: (value) => value.isEmpty ? LOGIN_SCREEN_EMAIL_WARNING : null,
onSaved: (value) => _email = value,
),
),
],
),
),
Divider(
height: 24.0,
color: Color(0x00000000),
),
new Row(
children: <Widget>[
new Expanded(
child: new Padding(
padding: const EdgeInsets.only(left: 40.0),
child: new Text(
LOGIN_SCREEN_PASSWORD,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color.fromRGBO(58, 66, 86, 1.0),
fontSize: 15.0,
),
),
),
),
],
),
new Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(left: 40.0, right: 40.0, top: 10.0),
alignment: Alignment.center,
padding: const EdgeInsets.only(left: 0.0, right: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Expanded(
child: TextFormField( // LOGIN SCREEN PASSWORD
obscureText: true,
keyboardType: TextInputType.emailAddress,
maxLines: 1,
autofocus: false,
textAlign: TextAlign.left,
decoration: InputDecoration(
icon: Icon(Icons.lock_outline),
hintText: LOGIN_SCREEN_PASSWORD_HINT,
hintStyle: TextStyle(color: Colors.grey),
),
validator: (value) => value.isEmpty ? LOGIN_SCREEN_PASSWORD_WARNING : null,
onSaved: (value) => _password = value,
),
),
],
),
),
Divider(
height: 24.0,
color: Color(0x00000000),
),
new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: new FlatButton(
child: new Text(
LOGIN_SCREEN_FORGOT_PASSWORD,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color.fromRGBO(58, 66, 86, 1.0),
fontSize: 15.0,
),
textAlign: TextAlign.end,
),
onPressed: () => StaticMethods.locked(context),
),
),
],
),
new Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(left: 30.0, right: 30.0, top: 20.0),
alignment: Alignment.center,
child: new Row(
children: <Widget>[
new Expanded(
child: new FlatButton( // LOGIN SCREEN LOGIN BUTTON
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0),
),
color: Color.fromRGBO(58, 66, 86, 1.0),
onPressed: () => _validateAndSubmit(),
child: new Container(
padding: const EdgeInsets.symmetric(
vertical: 20.0,
horizontal: 20.0,
),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Expanded(
child: Text(
LOGIN_SCREEN_LOGIN,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
],
),
),
),
),
],
),
),
Divider(
height: 25.0,
color: Color(0x00000000),
),
_showErrorMessage(),
Divider(
height: 25.0,
color: Color(0x00000000),
),
_showLoading(),
],
),
);
}
Widget SignupPage() {
return new Container(
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
color: Colors.white,
image: DecorationImage(
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.05), BlendMode.dstATop),
image: AssetImage('assets/images/drones.jpg'),
fit: BoxFit.cover,
),
),
child: new Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(100.0),
child: Center(
child: Icon(
Icons.school,
color: Color.fromRGBO(58, 66, 86, 1.0),
size: 50.0,
),
),
),
new Row(
children: <Widget>[
new Expanded(
child: new Padding(
padding: const EdgeInsets.only(left: 40.0),
child: new Text(
LOGIN_SCREEN_EMAIL,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color.fromRGBO(58, 66, 86, 1.0),
fontSize: 15.0,
),
),
),
),
],
),
new Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(left: 40.0, right: 40.0, top: 10.0),
alignment: Alignment.center,
padding: const EdgeInsets.only(left: 0.0, right: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Expanded(
child: TextField( //SIGNUP SCREEN EMAIL
obscureText: true,
textAlign: TextAlign.left,
decoration: InputDecoration(
hintText: LOGIN_SCREEN_EMAIL_HINT,
hintStyle: TextStyle(color: Colors.grey),
),
),
),
],
),
),
Divider(
height: 24.0,
color: Color(0x00000000),
),
new Row(
children: <Widget>[
new Expanded(
child: new Padding(
padding: const EdgeInsets.only(left: 40.0),
child: new Text(
LOGIN_SCREEN_PASSWORD,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color.fromRGBO(58, 66, 86, 1.0),
fontSize: 15.0,
),
),
),
),
],
),
new Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(left: 40.0, right: 40.0, top: 10.0),
alignment: Alignment.center,
padding: const EdgeInsets.only(left: 0.0, right: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Expanded(
child: TextField( // SIGNUP SCREEN PASSWORD
obscureText: true,
textAlign: TextAlign.left,
decoration: InputDecoration(
hintText: LOGIN_SCREEN_PASSWORD_HINT,
hintStyle: TextStyle(color: Colors.grey),
),
),
),
],
),
),
Divider(
height: 24.0,
color: Color(0x00000000),
),
new Row(
children: <Widget>[
new Expanded(
child: new Padding(
padding: const EdgeInsets.only(left: 40.0),
child: new Text( // SIGNUP SCREEN CONFIRM PASSWORD
LOGIN_SCREEN_CONFIRM_PASSWORD,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color.fromRGBO(58, 66, 86, 1.0),
fontSize: 15.0,
),
),
),
),
],
),
new Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(left: 40.0, right: 40.0, top: 10.0),
alignment: Alignment.center,
padding: const EdgeInsets.only(left: 0.0, right: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Expanded(
child: TextField(
obscureText: true,
textAlign: TextAlign.left,
decoration: InputDecoration(
hintText: LOGIN_SCREEN_PASSWORD_HINT,
hintStyle: TextStyle(color: Colors.grey),
),
),
),
],
),
),
Divider(
height: 24.0,
color: Color(0x00000000),
),
new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: new FlatButton(
child: new Text(
LOGIN_SCREEN_HAVE_ACCOUNT,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color.fromRGBO(58, 66, 86, 1.0),
fontSize: 15.0,
),
textAlign: TextAlign.end,
),
onPressed: () => StaticMethods.locked(context),
),
),
],
),
new Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(left: 30.0, right: 30.0, top: 50.0),
alignment: Alignment.center,
child: new Row(
children: <Widget>[
new Expanded(
child: new FlatButton( // SIGNUP SCREEN SIGN UP BUTTON
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0),
),
color: Color.fromRGBO(58, 66, 86, 1.0),
onPressed: () => StaticMethods.locked(context),
child: new Container(
padding: const EdgeInsets.symmetric(
vertical: 20.0,
horizontal: 20.0,
),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Expanded(
child: Text(
LOGIN_SCREEN_SIGN_UP,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
],
),
),
),
),
],
),
),
],
),
);
}
PageController _controller = new PageController(initialPage: 1, viewportFraction: 1.0);
#override
Widget build(BuildContext context) {
_isIos = Theme.of(context).platform == TargetPlatform.iOS;
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: new Form (
key: _formKey,
child: PageView(
controller: _controller,
physics: new AlwaysScrollableScrollPhysics(),
children: <Widget>[LoginPage(), OptionPage(), SignupPage()],
scrollDirection: Axis.horizontal,
onPageChanged: (num){
switch (num) {
case 0:
setState(() {
_formKey.currentState.reset();
_errorMessage = "";
_formMode = FormMode.LOGIN;
});
break;
case 1:
setState(() {
_formKey.currentState.reset();
_errorMessage = "";
_formMode = FormMode.OPTIONS;
});
break;
case 2:
setState(() {
_formKey.currentState.reset();
_errorMessage = "";
_formMode = FormMode.SIGNUP;
});
break;
}
},
),
),
);
}
Error:
I/flutter (18510): The following assertion was thrown during performLayout():
I/flutter (18510): RenderCustomMultiChildLayoutBox object was given an infinite size during layout.
.....
In the error, it shows just a blank page instead of lesson page, and checking firebase I can see that a user logs in so I think it is some layout issue.
What I've tried:
RootPage (I still saw a blank screen):
return new LessonPage(
title: LESSON_PAGE_TITLE,
userId: _userId,
auth: widget.auth,
onSignedOut: _onSignedOut,
);
I tried to return a normal page instead of lesson page which just shows a loading animation which is already tested and works but still I see a blank page:
return _buildWaitingScreen();
LessonPage (still see blank page):
Widget makeBody(BuildContext context) => Container(
height: MediaQuery.of(context).size.height / 1.5,
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
return makeCard(lessons[index]);
},
),
);
return Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: topAppBar,
body: makeBody(context),
bottomNavigationBar: makeBottom,
);
You dont need Container and SingleChildScrollView in below snippet:
return LessonPage(
title: LESSON_PAGE_TITLE,
userId: _userId,
auth: widget.auth,
onSignedOut: _onSignedOut,
);
If that didn't fix,
RenderCustomMultiChildLayoutBox object was given an infinite size during layout. means that you have used ListView or ScrollView or any other widget that has infinite size. In order to prevent this issue, wrap your makeBody list view with fixed size. If it is working you can use MediaQuery.of(context).size.height(gives device screen width) and adjest the size you want.
Ex:
Widget makeBody(BuildContext context) => Container(
height: MediaQuery.of(context).size.height / 1.5,
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: lessons.length,
itemBuilder: (BuildContext context, int index) {
return makeCard(lessons[index]);
},
),
);
and call the method: body: makeBody(context),
Fix in RootPage:
return new Container(
height: MediaQuery.of(context).size.height,
child: LessonPage(
title: LESSON_PAGE_TITLE,
userId: _userId,
auth: widget.auth,
onSignedOut: _onSignedOut,
)
);

How to position elements inside a ListView?

Good morning. I made this screen and it works like a charm on high resolutions. Problem is, I want to support lower resolutions like Nexus 4 (768x1280).
So when I run this on an emulator (Nexus 4 size) and I touch the input fields, the keyboard either: blocks the inputs, or moves the two buttons at the bottom and overlaps them on something else.
So, to solve it I wrapped the entire layout on a ListView(), but now the bottom buttons, which are wrapped on a Row(), just won't stay at the bottom.
This is my code without the ListView and working on high res, but not on low:
return Form(
Stack(
Center(
Column(
...
),
),
Align(
alignment: Alignment.bottomCenter,
child: Row(
...
),
),
),
);
This is how it looks with resizeToAvoidBottomPadding: false
And this is how it looks with the value set to true
Thanks, everyone.
Did you try to use SingleChildScrollView ?
It is a widget for making the page able to resize the height and as well scrolling , for the listview thats not what is the listview for , you should add listview when you get an unknown amount of data,
We don't normally use it to just adjust the view.
ok so i edited the code as in here and i can scroll normally after the keyboard pop up and the view is good and i can click on the button normally try it.
Widget formWidget(){
return new Scaffold(
// appBar: AppBar(
// // Here we take the value from the MyHomePage object that was created by
// // the App.build method, and use it to set our appbar title.
// title: Text(widget.title),
// ),
body:Column(
children: <Widget> [
Expanded(
child:SingleChildScrollView(
child: Form(
child:
// Stack(
// children: <Widget>[
// Center(
// child: SingleChildScrollView(
// child:
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: (MediaQuery.of(context).size.height) > 600
? const EdgeInsets.only(top: 0.0)
: const EdgeInsets.only(top: 30.0),
child: Image(
image: AssetImage('assets/favIcon.png'),
width: 88.0,
height: 88.0,
),
),
Padding(
padding: const EdgeInsets.fromLTRB(0, 28.0, 0, 12.0),
child: Text(
'Delegados',
style: TextStyle(
fontFamily: 'Archia',
fontSize: 32.0,
),
),
),
Text(
'Introduce tus datos de acceso aquí.',
style: TextStyle(
color: Color(0xff83868F),
fontFamily: 'Brutal',
fontSize: 14.0,
),
),
Padding(
padding:
const EdgeInsets.fromLTRB(16.0, 26.0, 16.0, 12.0),
child: TextFormField(
decoration: InputDecoration(
labelText: 'Correo',
filled: true,
fillColor: Color(0xffF0F1F5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
width: 0,
style: BorderStyle.none,
),
),
),
style: TextStyle(
fontFamily: 'Brutal',
color: Color(0xff1A1B1F),
),
controller: TextEditingController() ,
textInputAction: TextInputAction.next,
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 12.0),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'Contraseña',
filled: true,
fillColor: Color(0xffF0F1F5),
suffixIcon: Icon(Icons.remove_red_eye),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
width: 0,
style: BorderStyle.none,
),
),
),
style: TextStyle(
fontFamily: 'Brutal',
color: Color(0xff1A1B1F),
),
controller: TextEditingController() ,
//focusNode:FocusNode(),
obscureText: true,
),
),
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 26.0),
child: CupertinoButton(
onPressed: () {},
child: Text(
'Olvidé mi Contraseña',
style: TextStyle(
color: Color(0xff565A66),
fontFamily: 'Brutal',
fontSize: 14.0,
),
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0),
child: ButtonTheme(
minWidth: double.infinity,
child: InkWell(
// onTap: state is! LoginLoading
// ? _onLoginButtonPressed
// : null,
child: Container(
height: 48.0,
width: 500.0,
decoration: BoxDecoration(
color: Color(0xff00CC36),
borderRadius: BorderRadius.circular(8.0),
),
child: Align(
alignment: Alignment.center,
child: Container(
child:
// state is LoginLoading
// ? CircularProgressIndicator():
Text(
'INGRESAR AHORA ›',
style: TextStyle(
color: Colors.white,
fontFamily: 'Brutal',
fontSize: 14.0,
),
),
)),
),
),
),
),
],
),
// ),
)
,
// aqui va <<<<<<<<<<<<
// ],
// ),
)
),
Align(
alignment: Alignment.bottomCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CupertinoButton(
onPressed: () {},
child: Text(
'¿No tienes cuenta?',
style: TextStyle(
color: Color(0xff83868F),
fontFamily: 'Brutal',
fontSize: 14.0,
),
),
),
InkWell(
onTap: null,
child: Container(
height: 32.0,
width: 112.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
border: Border.all(
color: Color(0xffD7D9E0),
width: 1.0,
),
),
child: Align(
alignment: Alignment.center,
child: Text(
'CONTACTANOS',
style: TextStyle(
color: Color(0xff565A66),
fontFamily: 'Brutal',
fontSize: 11.0),
),
),
),
),
],
),
),
]
)
,
// )
);
}
In your Scaffold can you try this.
Scaffold(
resizeToAvoidBottomInset: false,
...
);
I position two elements in the center and bottom of the screen by this code.
body: Column(
children: [
Expanded(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 80,
width: 80,
child:
Image(image: AssetImage("assets/images/logo.png"))),
SizedBox(height: 10),
Text(
"Tings App",
style: TextStyle(fontSize: 36),
),
SizedBox(height: 10),
TextFormField(
controller: _emailController,
decoration: InputDecoration(
border: OutlineInputBorder(),
isDense: true,
labelText: 'Email/phone'),
validator: (value) {
if (value.isEmpty) {
return 'Please enter a valid email';
}
return null;
},
),
],
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
_isLoading
? CircularProgressIndicator()
: SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
height: 60.0,
child: ElevatedButton(
style: ButtonStyle(
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
),
),
),
child: Text(
"SignIn",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w800,
fontSize: 18),
),
onPressed: () {
_login(context);
},
),
),
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Don't have account?",
style: TextStyle(fontSize: 16),
),
SizedBox(width: 6),
TextButton(
onPressed: () {
Navigator.push(
context, SlideRightRoute(page: Signup()));
},
child: Text("SignUp"),
),
],
)
],
),
),
)
],
),

Flutter: How to create a row with a centered word with a line around it?

Trying to accomplish the below effect:
This is the code I have:
new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Divider(
color: const Color(0xFF000000),
height: 20
),
new Text(
"OR",
style: new TextStyle(
fontSize: 20.0,
color: const Color(0xFF000000),
fontWeight: FontWeight.w200,
),
),
const Divider(
color: const Color(0xFF000000),
height: 20
),
]),
The word OR appears in the middle but both Dividers are hidden? (However, if I play with the height you can see the height of the row increase/decrease but still no divider. How can I get this effect?
use this widget, it will do exactly what you want
class OrComponent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
child: Container(
height: 2.0,
color: AppColor.lighterGrey,
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 3.0),
child: Text("OR",style:TextStyle(color:Colors.black)),
),
Expanded(
child: Container(
height: 2.0,
color: Colors.grey,
),
),
],
);
}
}
You can use the Expanded widget on the two Dividers (and on the Text for coherence purposes)
Expanded(
flex: 1,
child: Divider(
color: const Color(0xFF000000),
height: 2,
)),
Expanded(
flex: 0,
child: Container(
margin: EdgeInsets.symmetric(horizontal: 15.0),
child: Text(
"OR",
style: new TextStyle(
fontSize: 20.0,
color: const Color(0xFF000000),
fontWeight: FontWeight.w200,
),
))),
Expanded(
flex: 1,
child: Divider(
color: const Color(0xFF000000),
height: 2,
))

Card overlapping raised button in flutter

friends, I am thinking to make this type of view but I can't able to set the button overlapping like the given image I am using stack widget which is containing the text fields and the buttons as given image please check and help me out I also tried to use the center widgets as well but the view is coming as required in it also i had used the positioned widget but its getting button bottom of the screen like this but i need as the above image
MyLayoutDesign
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
MyAppState myAppState() => new MyAppState();
return myAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return new MaterialApp(home: new Scaffold(body: new Builder(
builder: (BuildContext context) {
return new Stack(
children: <Widget>[
new Image.asset(
'assets/images/bg.png',
fit: BoxFit.cover,
),
new Center(
child: new Container(
child: new Card(
color: Colors.white,
elevation: 6.0,
margin: EdgeInsets.only(right: 15.0, left: 15.0),
child: new Wrap(
children: <Widget>[
Center(
child: new Container(
margin: EdgeInsets.only(top: 20.0),
child: new Text(
'Login',
style: TextStyle(
fontSize: 25.0, color: secondarycolor),
),
),
),
new ListTile(
leading: const Icon(Icons.person),
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'Please enter email',
labelText: 'Enter Your Email address',
),
keyboardType: TextInputType.emailAddress,
),
),
new ListTile(
leading: const Icon(Icons.lock),
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'Please enter password',
labelText: 'Enter Your Password',
),
keyboardType: TextInputType.emailAddress,
obscureText: true,
),
),
Container(
margin: EdgeInsets.only(top: 10.0, bottom: 15.0),
child: Center(
child: Text(
"FORGOT PASSWORD",
style: TextStyle(
decoration: TextDecoration.underline,
color: Colors.black,
fontSize: 16.0),
),
),
),
Center(
child: Container(
margin: EdgeInsets.only(bottom: 40.0, top: 10.0),
child: Text.rich(
TextSpan(
children: const <TextSpan>[
TextSpan(
text: 'NEW USER ? ',
style: TextStyle(
fontSize: 16.0, color: Colors.black)),
TextSpan(
text: 'REGISTER',
style: TextStyle(
fontSize: 16.0,
decoration: TextDecoration.underline,
color: Colors.black)),
],
),
),
),
),
],
),
),
),
),
new RaisedButton(
onPressed: () {
print('Login Pressed');
},
color: primarycolor,
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
child: new Text('Login',
style: new TextStyle(
color: Colors.white,
fontSize: 16.0,
fontWeight: FontWeight.bold)),
),
],
);
},
)));
}
}
this is just one the many ways you can achieve the expected result.
In this case, i assume you know the height of the background.
Again, there are many ways to achieve what you want. There is nothing wrong with your code, you just have to get an understanding of how 'things' work in Flutter
Widget demo = Stack(
children: <Widget>[
//First thing in the stack is the background
//For the backgroud i create a column
Column(
children: <Widget>[
//first element in the column is the white background (the Image.asset in your case)
DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: Colors.white
),
child: Container(
width: 300.0,
height: 400.0,
)
),
//second item in the column is a transparent space of 20
Container(
height: 20.0
)
],
),
//for the button i create another column
Column(
children:<Widget>[
//first element in column is the transparent offset
Container(
height: 380.0
),
Center(
child: FlatButton(
color: Colors.red,
child: Text("Press Me"),
onPressed: () {},
),
)
]
)
],
);
Here you can find your solution code below.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
MyAppState myAppState() => new MyAppState();
return myAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return new MaterialApp(home: new Scaffold(body: new Builder(
builder: (BuildContext context) {
return new Stack(
children: <Widget>[
new Image.asset(
'assets/images/bg.jpeg',
fit: BoxFit.fitWidth,
),
new Center(
child: new Container(
height: 370.0,
child: Container(
height:250.0,
child: new Card(
color: Colors.white,
elevation: 6.0,
margin: EdgeInsets.only(right: 15.0, left: 15.0),
child: new Wrap(
children: <Widget>[
Center(
child: new Container(
margin: EdgeInsets.only(top: 20.0),
child: new Text(
'Login',
style: TextStyle(
fontSize: 25.0, color: Colors.red),
),
),
),
new ListTile(
leading: const Icon(Icons.person),
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'Please enter email',
labelText: 'Enter Your Email address',
),
keyboardType: TextInputType.emailAddress,
),
),
new ListTile(
leading: const Icon(Icons.lock),
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'Please enter password',
labelText: 'Enter Your Password',
),
keyboardType: TextInputType.emailAddress,
obscureText: true,
),
),
Container(
margin: EdgeInsets.only(top: 10.0, bottom: 15.0),
child: Center(
child: Text(
"FORGOT PASSWORD",
style: TextStyle(
decoration: TextDecoration.underline,
color: Colors.black,
fontSize: 16.0),
),
),
),
Center(
child: Container(
margin: EdgeInsets.only(bottom: 40.0, top: 10.0),
child: Text.rich(
TextSpan(
children: const <TextSpan>[
TextSpan(
text: 'NEW USER ? ',
style: TextStyle(
fontSize: 16.0, color: Colors.black)),
TextSpan(
text: 'REGISTER',
style: TextStyle(
fontSize: 16.0,
decoration: TextDecoration.underline,
color: Colors.black)),
],
),
),
),
),
Padding(padding: EdgeInsets.only(left: 120.0)),
],
),
),
),
padding: EdgeInsets.only(bottom: 30),
),
),
new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 310.0)),
RaisedButton(
onPressed: () {
print('Login Pressed');
},
color: Colors.green,
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
child: new Text('Login',
style: new TextStyle(
color: Colors.white,
fontSize: 16.0,
fontWeight: FontWeight.bold)),
),
],
)
)
],
);
},
)));
}
}
#ibhavikmakwana propsed a better solution to your question. The other answers all depend on the size of the background and are not screen independent. They both create an invisible object above the button (either by adding a Container or a Padding).
I was having that question too and did not find your question first.
His simple solution is to wrap the button in a Positioned widget and give it a bottom of 0 or < 0.
Positioned(
child: FlatButton(
color: Colors.red,
child: Text("Press Me"),
onPressed: () {},
),
right: 0,
left: 0,
bottom: 0,
)
I found out that the "bottom" attribute set to 0 will offset the widget by exactly 0.5*height of the widget.

Resources