Related
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,
)
);
Im fairly new to flutter,
I have created a nice BottomAppBar with a docked FAB however i also want to use this AppBar for page navigation. I've tried it with a BottomNavigationBar but then i lose the docked floating action button. How can i implement navigation into the bottom app bar??
floatingActionButton: Container(
height: 65.0,
width: 65.0,
child: FittedBox(
child: FloatingActionButton(
onPressed: (){},
child: Icon(Icons.add, color: Colors.white,),
// elevation: 5.0,
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: BottomAppBar(
// elevation: 20.0,
shape: CircularNotchedRectangle(),
child: Container(
height: 75,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
iconSize: 30.0,
padding: EdgeInsets.only(left: 28.0),
icon: Icon(Icons.home),
onPressed: () {
setState(() {
currentIndex = 0;
});
},
),
IconButton(
iconSize: 30.0,
padding: EdgeInsets.only(right: 28.0),
icon: Icon(Icons.search),
onPressed: () {
setState(() {
currentIndex = 1;
print("${currentIndex}");
});
},
),
IconButton(
iconSize: 30.0,
padding: EdgeInsets.only(left: 28.0),
icon: Icon(Icons.notifications),
onPressed: () {
setState(() {
currentIndex = 2;
print("${currentIndex}");
});
},
),
IconButton(
iconSize: 30.0,
padding: EdgeInsets.only(right: 28.0),
icon: Icon(Icons.list),
onPressed: () {
setState(() {
currentIndex = 3;
print("${currentIndex}");
});
},
)
],
),
)
)
One Way of Doing it is with - PageView widget.
Example Code with your Coded BottomAppBar.
class _DemoPageState extends State<FormPage> {
PageController _myPage = PageController(initialPage: 0);
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
child: Container(
height: 75,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
iconSize: 30.0,
padding: EdgeInsets.only(left: 28.0),
icon: Icon(Icons.home),
onPressed: () {
setState(() {
_myPage.jumpToPage(0);
});
},
),
IconButton(
iconSize: 30.0,
padding: EdgeInsets.only(right: 28.0),
icon: Icon(Icons.search),
onPressed: () {
setState(() {
_myPage.jumpToPage(1);
});
},
),
IconButton(
iconSize: 30.0,
padding: EdgeInsets.only(left: 28.0),
icon: Icon(Icons.notifications),
onPressed: () {
setState(() {
_myPage.jumpToPage(2);
});
},
),
IconButton(
iconSize: 30.0,
padding: EdgeInsets.only(right: 28.0),
icon: Icon(Icons.list),
onPressed: () {
setState(() {
_myPage.jumpToPage(3);
});
},
)
],
),
),
),
body: PageView(
controller: _myPage,
onPageChanged: (int) {
print('Page Changes to index $int');
},
children: <Widget>[
Center(
child: Container(
child: Text('Empty Body 0'),
),
),
Center(
child: Container(
child: Text('Empty Body 1'),
),
),
Center(
child: Container(
child: Text('Empty Body 2'),
),
),
Center(
child: Container(
child: Text('Empty Body 3'),
),
)
],
physics: NeverScrollableScrollPhysics(), // Comment this if you need to use Swipe.
),
floatingActionButton: Container(
height: 65.0,
width: 65.0,
child: FittedBox(
child: FloatingActionButton(
onPressed: () {},
child: Icon(
Icons.add,
color: Colors.white,
),
// elevation: 5.0,
),
),
),
);
}
}
The difference between the BottomAppBar and the BottomNavigationBar, is that with the last one, you can set a list of children (pages) to be rendered as you click on the icons below (onTap method). With the BottomAppBar, you have to set a Navigator method, speaking in UI/UX terms, I don't think it's very beauty to see.
Create an auxiliar component, which will have the BottomAppBar.
Then, pass a Row as the child method of it
Fill with your IconButtons
Set the onPressed methods to call the pages (Navigator.of(context).pushName('/yourScreenHere')
Then, for every screen you make you can add an AppBar on them.
You can use a switch case for your body using the same scaffold - Like in tabcontroller or radiobutton.
Just update the body when bottomAppBar icon is pressed.
Check out this link for better understanding. :)
I have form to grab info from User. But when I tap on TextFormField and keyboard shows it disappears.
https://youtu.be/UhVL2hqWOlQ
Here is my code:
class _AddInfoState extends State<AddInfo>{
static final GlobalKey<FormState> _key = new GlobalKey();
bool _validate = false;
String strTitle;
File fileImage;
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body:new Container(
child: new Form(
key: _key,
autovalidate: _validate,
child: formUI(),
),
),
);
}
Widget formUI(){
return new ListView(
children: <Widget>[
//Title Field
new Container(
padding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 0.0),
child:new TextFormField(
style: styleTextField,
keyboardType: TextInputType.text,
autofocus: true,
maxLength: 45,
decoration: InputDecoration(
hintText: 'Enter Title',
prefixIcon: Icon(
Icons.title,
size: 30.0,
color: blueColor,
)
),
validator: validateTitle,
onSaved: (String val) {
strTitle = val;
},
),
),
],
physics: new ClampingScrollPhysics(),
);
}
By my understanding from the video, since you have used a scrollable ListView inside your form, the list is scrolling upward once the keyboard appears hence it disappears.
Try setting resizeToAvoidBottomPadding: false in your Scaffold. This will make the keyboard appear as an overlay and won't push the content upward.
Example:
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
resizeToAvoidBottomPadding: false,
body:new Container(
child: new Form(
key: _key,
autovalidate: _validate,
child: formUI(),
),
),
);
}
Let me know if this helps!
Wrap the Container which has the TextFormField in a SizedBox :
new Form(
autovalidate: false,
key: _formKey,
child: new Container(
height: orientation == Orientation.portrait ? MediaQuery.of(context).size.height : MediaQuery.of(context).size.width/1.2 ,
padding: EdgeInsets.only(left: 10.0, right: 10.0),
child: new Column(
//mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new SizedBox(
width: MediaQuery.of(context).size.width,
height: 100.0,
child: new Container(
padding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 0.0),
child:new TextFormField(
style: styleTextField,
keyboardType: TextInputType.text,
autofocus: true,
maxLength: 45,
decoration: InputDecoration(
hintText: 'Enter Title',
prefixIcon: Icon(
Icons.title,
size: 30.0,
color: blueColor,
)
),
validator: validateTitle,
onSaved: (String val) {
strTitle = val;
},
),
),
)
]
)
)
]
)
)
)
Set the resizeToAvoidBottomPadding: false and then change the Column to a ListView
I'm trying to make a bottomsheet that has a text field and autofocus is set to true so that the keyboard pops up. But, bottomsheet is overlapped by the keyboard. Is there a way to move bottomsheet above the keyboard?
Padding(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(children: <Widget>[
TextField(
autofocus: true,
decoration: InputDecoration(hintText: 'Title'),
),
TextField(
decoration: InputDecoration(hintText: 'Details!'),
keyboardType: TextInputType.multiline,
maxLines: 4,
),
TextField(
decoration: InputDecoration(hintText: 'Additional details!'),
keyboardType: TextInputType.multiline,
maxLines: 4,
),]);
To fix this issue
All you need is to use Keyboard padding using MediaQuery.of(context).viewInsets.bottom
For more insurance, set isScrollControlled = true of the BottomSheetDialog this will allow the bottom sheet to take the full required height.
Note
If your BottomSheetModel is Column make sure you add mainAxisSize: MainAxisSize.min, otherwise the sheet will cover the whole screen.
Works with multiple TextField or TextFormField
Example
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(25.0))),
backgroundColor: Colors.black,
context: context,
isScrollControlled: true,
builder: (context) => Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Text('Enter your address',
style: TextStyles.textBody2),
),
SizedBox(
height: 8.0,
),
TextField(
decoration: InputDecoration(
hintText: 'adddrss'
),
autofocus: true,
controller: _newMediaLinkAddressController,
),
SizedBox(height: 10),
],
),
));
Please note that:
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(25.0))),
It's not required. It's just that I'm creating a rounded bottom sheet.
UPDATED
Flutter 2.2 breaks the changes again!” Now you need to give bottom padding once again so the keyboard doesn't overlap the bottomsheet.
Just add:
isScrollControlled: true to showModalBottomSheet
padding: MediaQuery.of(context).viewInsets to widget in builder
column/wrap both works
showModalBottomSheet<void>(
isScrollControlled: true,
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0)),
),
builder: (BuildContext context) {
return Padding(
padding: MediaQuery.of(context).viewInsets,
child: Container(
child: Wrap(
children: <Widget>[
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term'),
),
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term'),
),
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term'),
),
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term'),
)
],
)));
},
);
Update Feb 25 2020 better solution
showModalBottomSheet(
isScrollControlled: true,
builder: (BuildContext context) {
return SingleChildScrollView(
child: Container(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Padding(
padding: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0), // content padding
child: Form(...)) // From with TextField inside
});
In Order to focus on the Keyboard in BottomSheet - Wrap TextField in Padding Widget as like Below e.g Code:
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: TextField(
autofocus: true,
),
),
);
});
In the latest version of flutter, you can move your bottomSheet using isScrollControlled property/named parameter. Suppose i have a function(_showModal) which will invoke when a button is pressed. And i define the bottom sheet functionality on that function.
void _showModal() {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Column(
children: <Widget>[
TextField(// your other code),
SizedBox(height: 5.0),
TextField(// your other code),
SizedBox(height: 5.0),
TextField(// your other code),
]
);
},
);
}
Here a ModalBottomSheet will appear but with fullscreen of height. And you don't need that height. So, you need to Column's mainAxisSize to min.
Column(
mainAxisSize: MainAxisSize.min,
// your other code
)
Fullscreen of height problem is solved, but ModalBottomSheet doesn't move to top when keyboard is appeared. Ok, to solve this issue you need to set viewInsets bottom padding to your ModalBottomSheet. So to set padding we need to wrap our Column with Container or Padding and then set the padding. The final code will look like this
void _showModal() {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
// You can wrap this Column with Padding of 8.0 for better design
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(// your other code),
SizedBox(height: 5.0),
TextField(// your other code),
SizedBox(height: 5.0),
TextField(// your other code),
]
),
);
},
);
}
Hope your issue is fixed. Thank's 🙏
Package: https://pub.dev/packages/modal_bottom_sheet
Wrap your widget into Padding and set padding like this ==>
padding: MediaQuery.of(context).viewInsets // viewInsets will decorate your screen
You can use
showMaterialModalBottomSheet or showModalBottomSheet or showCupertinoModalBottomSheet
showModalBottomSheet(
context: context,
barrierColor: popupBackground,
isScrollControlled: true, // only work on showModalBottomSheet function
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(borderRadiusMedium),
topRight: Radius.circular(borderRadiusMedium))),
builder: (context) => Padding(
padding: MediaQuery.of(context).viewInsets,
child: Container(
height: 400, //height or you can use Get.width-100 to set height
child: <Your Widget here>
),)),)
Try this
My Solution is
Use isScrollControlled: true
Add Padding
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom)
Wrap your layout in SingleChildScrollView
SAMPLE CODE
Future<void> future = showModalBottomSheet(
context: context,
isDismissible: true,
isScrollControlled: true,
backgroundColor: Colors.white.withOpacity(0.2),
builder: (context) => SingleChildScrollView(
child: GestureDetector(
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom
),
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
children: <Widget>[
// add your widget here
],
),
),
)
),
)
);
I got it fixed by increasing the height of the child widget when the keyboard gets opened.
initially value of MediaQuery.of(context).viewInsets.bottom will be 0 it will change when the keyboard gets focused.
showModalBottomSheet<void>(
enableDrag: true,
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Card(
color: Colors.white,
child: Container(
height: MediaQuery.of(context).size.height / 2 +
MediaQuery.of(context).viewInsets.bottom,
child: Column(
children: <Widget>[
TextField(),
TextField(),
],
),
),
);
},
);
UPDATED 2021 May flutter 2.2!
Now you need to give bottom padding. The below written was a bug.
UPDATED 2020!
This answer is true, but you don't have to give bottom padding now!
Find and delete this line:
padding: MediaQuery.of(context).viewInsets
For people who can't solve their problem trying all the answers. These answers are correct but not so clear.
When using
MediaQuery.of(context).viewInsets.bottom)
make sure your context variable is using the one provided by bottom sheet builder property.
builder :(**c**)=>MediaQuery.of(**c**)
Try this.
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) {
return AnimatedPadding(
padding: MediaQuery.of(context).viewInsets,
duration: const Duration(milliseconds: 100),
curve: Curves.decelerate,
child: Container(
child: Wrap(
children: [
TextField(
decoration: InputDecoration(labelText: "1"),
),
TextField(
decoration: InputDecoration(labelText: "2"),
),
TextField(
decoration: InputDecoration(labelText: "3"),
),
],
)));
},
)
Add this after the last widget of your bottom sheet
Padding(padding: EdgeInsets.only(bottom:MediaQuery.of(context).viewInsets.bottom))
You should to use this then,
showModalBottomSheet(
isScrollControlled: true,
context: context,
shape: RoundedRectangleBorder(
// <-- for border radius
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0),
),
),
builder: (BuildContext context) {
return SingleChildScrollView(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: drunkenWidget()...
//BTW, Never ever Drink
If you have full screen or fixed size showModalBottomSheet don't use padding it will not solve your problem. Use margin instead of padding like this :
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
marign: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: TextField()
);
});
Instead of using builder: (BuildContext context) { } in the builder, use builder: (context) { }
With this solution my modal bottom sheet sticks to the statusbar (acts like Scaffold with resizeToAvoidBottomInset: false) and allows to view all the form fields and scroll through the form if it is still needed to view bottom text fields.
For more details, here's the link from where I found the solution- https://github.com/flutter/flutter/issues/18564#issuecomment-602604778
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
context: context,
isScrollControlled: true,
builder: (builder) {
return Container(
height: MediaQuery.of(context).size.height - 40,
padding: MediaQuery.of(context).viewInsets,
child: <Your Widget Here>,
);
},
);
After combining different solutions I got this:
if you don't want it to be Full screen and don't want to use the Padding workaround use
showModalBottomSheet(
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
enableDrag: true,
isDismissible: true,
useRootNavigator: true,
builder: (BuildContext ctx) {
return Scaffold( // use CupertinoPageScaffold for iOS
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: true, // important
body: SingleChildScrollView(
child: Form(
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormField(),
TextFormField(),
],
),
),
),
),
);
},
);
on Flutter (Channel master, v1.15.3-pre.37, on Mac OS X 10.15.2 19C57, locale en-US)
Fount this on github
Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: TextField()
)
If you still have not found your issue. So I think you have missed in your BuilderContext. Sometimes when you implement modalBottomSheet it will give you only the context parameter. So add context with BuildContext.
builder: (BuildContext context) { //-Here is your issue add BuilderContext class name as it as
return Padding(
padding: MediaQuery.of(context).viewInsets,
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: new Container(
Wrap the Form with a Scaffold widget, and then wrap the TextFormField with a SingleChildScrollView :
return Container(
height: screenHeight * .66,
child: Scaffold(
body: Form(
key: _form,
child: SingleChildScrollView(
child:TextFormField()
)
)
)
)
showModalBottomSheet(
isScrollControlled: true,
builder: (BuildContext context) {
return SingleChildScrollView(
child: Container(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Padding(
padding: const EdgeInsets.all(15.0), // content padding
child: Container())});
Note : This line does all the magic
Add this in your router link modal bottom sheet
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return Messagescr();
}
);
And remove this line also in your Scaffold :
resizeToAvoidBottomInset : false,
Simple Solution, you can customize this :
Container(
margin: EdgeInsets.only(left: 15),
child: InkWell(
onTap: () {
showModalBottomSheet(
isScrollControlled : true,
context: context,
backgroundColor: Colors.transparent,
builder: (context) {
return Container(
padding: EdgeInsets.only(top: 15, left: 15, right: 15, bottom: 10),
width: double.infinity,
decoration: BoxDecoration(
color: AppTheme.leadItemColor1,
borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)),
),
child: Column(
children: [
_assignTo(widget.viewModel, context),
SizedBox(height: 12,),
txtComment(widget.viewModel),
SizedBox(height: 12,),
CRMButton(
title: 'Select',
onTap: () async {
Navigator.pop(context);
await widget.viewModel.updateStatus(7, why: "${ConstantData.lostOptions[_selectedNumber]}");
},
)
],
),
);
},
);
},
child: CustomTabBarItem1(
image: widget.viewModel.leadDetail.success.lStatus == 7 ? 'assets/appimages/LeadDetail/icons-03-01.png' : 'assets/appimages/LeadDetail/icons-04-01.png',
bottomTitle: 'Lost',
topTitle: widget.viewModel.leadDetail.success.lStatus > 7 ? 'assets/appimages/LeadDetail/Ellipse 61#2x.png' : widget.viewModel.leadDetail.success.lStatus == 7 ? 'assets/appimages/LeadDetail/Group 486-1.png' : 'assets/appimages/LeadDetail/Ellipse-61#3x.png',
height : widget.viewModel.leadDetail.success.lStatus == 7 ? "0" : "1",
)),
),
import 'dart:async';
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import '../weight/boostrap/flutter_bootstrap.dart';
import '../weight/boostrap/bootstrap_widgets.dart';
/*
TextEditingController txtname = TextEditingController();
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
builder: (context) => SingleChildScrollView(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom),
child: new AddItem(
tektk: 'Category',
tektd: 'Add',
txtname: txtname,
ismultik:false,
onPressed: () {}),
),
);
*/
class AddItem extends StatelessWidget {
const AddItem(
{Key? key,
required this.ismultik,
required this.tektd,
required this.tektk,
required this.txtname,
required this.onPressed})
: super(key: key);
final bool ismultik;
final String tektk;
final String tektd;
final VoidCallback? onPressed;
final TextEditingController txtname;
#override
Widget build(BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
bootstrapGridParameters(gutterSize: 10);
return Padding(
padding: mediaQueryData.viewInsets,
child: Container(
padding: EdgeInsets.only(bottom: 90.0, left: 10.0, right: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ListTile(
trailing: SizedBox.fromSize(
size: Size(35, 35),
child: ClipOval(
child: Material(
color: Colors.indigo,
child: InkWell(
splashColor: Colors.white,
onTap: () {
Navigator.pop(context);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.close, color: Colors.white),
],
),
),
),
),
),
),
BootstrapRow(height: 0, children: [
BootstrapCol(
sizes: 'col-md-12',
child: TextField(
style: TextStyle(color: Colors.black),
decoration: new InputDecoration(
border: new OutlineInputBorder(
borderSide: new BorderSide(color: Colors.white)),
labelText: tektk,
),
keyboardType: ismultik == true
? TextInputType.multiline
: TextInputType.text,
maxLines: null,
minLines: 1,
controller: txtname,
),
),
BootstrapCol(
sizes: 'col-md-12',
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.green, // background
onPrimary: Colors.white, // foreground
),
onPressed: onPressed,
child: Text(tektd)),
),
]),
],
),
),
);
}
}
Just add
if (_isEditing) SizedBox(height: MediaQuery.of(context).viewInsets.bottom),
under the keyboard and hide all other content below the textfield with
if (!_isEditing) Widget(...),
I want to my card button fit together like Reddit App. How can do that?
Outside the main Row has a Container and Container' has padding height 15.0 . How can Row's widget fit that height 15.0 responsively.
Reddit card buttons
My app card buttons
This is my code;
#override
Widget build(BuildContext context) {
return new SafeArea(
top: false,
bottom: false,
child: new Card(
child: new Column(
children: <Widget>[
new Container(
padding: EdgeInsets.fromLTRB(5.0, 15.0, 5.0, 3.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
color: Colors.blueGrey,
child: new Row(
children: <Widget>[
new Icon(Icons.arrow_drop_up),
new Text('Vote'),
new Icon(Icons.arrow_drop_down),
],
),
),
new Container(
color: Colors.blueGrey,
child: new Row(
children: <Widget>[
new Icon(Icons.mode_comment),
new Text('Comment'),
],
),
),
new Container(
color: Colors.blueGrey,
child: new Row(
children: <Widget>[
new Icon(Icons.share),
new Text('Share'),
],
),
),
],
),
)
],
),
),
);
}
Hello and welcome to Flutter :)
First of all you have used too much padding i.e. 15.0 so that is why your grey boxes are smaller than that of Reddit example.
I have taken a simpler approach and designed a sample control for you. Hope you like it.
import 'package:flutter/material.dart';
void main() {
runApp(RedditButtonsExample());
}
class RedditButtonsExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Reddit Buttons Example",
home: HomePage(),
theme: ThemeData(
primaryColor: Colors.white,
accentColor: Colors.white,
),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Reddit Buttons Example'),
),
body: Column(
children: <Widget>[
Expanded(child: Icon(Icons.insert_emoticon)),
RedditButtonsCard(), //Example widget.
],
),
);
}
}
//This is the example control that you need.
class RedditButtonsCard extends StatelessWidget {
const RedditButtonsCard({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Card(
color: Colors.black,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
FlatButton.icon(
textColor: Colors.white,
icon: Icon(
Icons.thumbs_up_down,
color: Colors.white,
),
label: Text('Vote'),
onPressed: () {},
),
FlatButton.icon(
color: Colors.white30,
textColor: Colors.white,
icon: Icon(
Icons.mode_comment,
color: Colors.white,
),
label: Text('Comment'),
onPressed: () {},
),
FlatButton.icon(
textColor: Colors.white,
icon: Icon(
Icons.share,
color: Colors.white,
),
label: Text('Share'),
onPressed: () {},
),
],
),
),
);
}
}
I used Table and TableRow and I found what I wanted. Off the topic but I want to say this; I found this solution in my dream. I said my self "you have to use DataTable or something then you got what you want." My subconscious full of with Flutter I guess. :D
#override
Widget build(BuildContext context) {
return new SafeArea(
top: false,
bottom: false,
child: new Card(
child: new Column(
children: <Widget>[
new Table(
children: [
new TableRow(
children: [
new InkWell(
onTap: () {},
child: Padding(
padding: const EdgeInsets.fromLTRB(5.0, 15.0, 5.0, 15.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Icon(FontAwesomeIcons.arrowAltCircleUp, color: Colors.white),
Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0),
child: new Text('Vote', style: TextStyle(color: Colors.white)),
),
new Icon(FontAwesomeIcons.arrowAltCircleDown, color: Colors.white),
],
),
),
),
new InkWell(
onTap: () {},
child: Padding(
padding: const EdgeInsets.fromLTRB(5.0, 15.0, 5.0, 15.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Icon(Icons.mode_comment, color: Colors.white),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: new Text('Comment', style: TextStyle(color: Colors.white)),
),
],
),
),
),
new InkWell(
onTap: () {},
child: Padding(
padding: const EdgeInsets.fromLTRB(5.0, 15.0, 5.0, 15.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Icon(Icons.share, color: Colors.white),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: new Text('Share', style: TextStyle(color: Colors.white)),
),
],
),
),
),
],
),
],
),
],
),
),
);
}