Child widget of CupertinoPageScaffold is behind CupertinoNavigationBar - flutter

I'm trying to put contents below a CupertinoNavigationBar.
But the content Widgets are partially covered by the CupertinoNavigationBar.
I don't understand why the child Column is not vertically offset so that the top isn't covered by the CupertinoNavigationBar.
Below is a screenshot, and my code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class PersonalInfoEditor extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: buildNavigationBar(context),
child: Column(
children: <Widget>[
Text('Personal Info'),
Text('t1'),
],
),
);
}
CupertinoNavigationBar buildNavigationBar(BuildContext context) {
return CupertinoNavigationBar(
trailing: CupertinoButton(
child: Text('Save', style: TextStyle(color: CupertinoColors.activeBlue)),
onPressed: () => Navigator.pop(context),
));
}
}

Wrap your child inside SafeArea :
return CupertinoPageScaffold(
navigationBar: buildNavigationBar(context),
child: SafeArea(
child: Column(
children: <Widget>[
Text('Personal Info'),
Text('t1'),
],
),
),
);
And don't forget to use CupertinoApp instead of MaterialApp
More info: https://api.flutter.dev/flutter/cupertino/CupertinoPageScaffold-class.html

Related

Flutter: How to have one tap handler for two widgets?

I've got an IconButton and Text as the children of a Row widget. Currently when user taps on the calendar icon, its onPressed is handled and a calendar is shown to pick a date. However I want to extend the tapping area and allow the calendar to open even when user taps on the Text widget. What's the best way to achieve this purpose?
Please note that there are more children in this Row. I only want to handle the tap on these two children.
InkWell might do the trick for you.
import 'package:flutter/material.dart';
class DoubleTapPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: InkWell(
child: Row(
children: [
Icon(Icons.calendar_today),
Text('2021 Jan 23')
],
),
onTap: () => print('Calendar or Date tapped'),
),
),
);
}
}
In response to the edit:
import 'package:flutter/material.dart';
class DoubleTapPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: [
Text('Outside the well'),
InkWell(
child: Row(
children: [
Icon(Icons.calendar_today),
Text('2021 Jan 23')
],
),
onTap: () => print('Calendar or Date tapped'),
),
],
),
),
);
}
}
Wrap the whole Row in GestureDetector as following
GestureDetector(
onTap: () {
// what your want here
}
child: Row(
// ...
),
),

How to align widgets in a GridView in Flutter?

I want to align the contents of a GridView item to the center (both vertically and horizontally) without changing the size of the widget.
This code creates a GridView with an item in it, it has a default top-center alignment and the size is a default square.
GridView.count(
crossAxisCount: 3,
children: [
Container(
// alignment: Alignment.center,
child: RaisedButton(
child: Column(
children: [
Text("Top text"),
Text("Bottom text"),
],
),
onPressed: (){}
),
),
],
);
And it looks like this:
When I uncomment the alignment, it doesn't align the items correctly and the size of the item changes, I don't want neither of them.
Note that I need multiple Widgets in that GridView item, so I can't remove the Column (but maybe I can replace it?).
I don't see any size change after adding mainAxisAlignment to the Column. Please run the code below :
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 3,
children: [
Container(
// alignment: Alignment.center,
child: RaisedButton(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Top text"),
Text("Bottom text"),
],
),
onPressed: () {}),
),
],
);
}
}

Flutter: Button should output an widget but doesn't

This is my function:
Widget RandomSkill(Widget SkillJonglieren) {
return Widget SkillJonglieren;
This is the stateless Widget "SkillJonglierten"
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Column(
children: <Widget>[
Text('Jonglieren'),
Image.asset('images/Jonglieren.jpg')
],
),
),
);
}
}
This is the button
body: Align(alignment: Alignment.bottomCenter,
child: RaisedButton(
onPressed: RandomSkill,
child: Text('Zufälliger Skill')
),
));
I just want, that when i press the button that this widget is shown
You need a StatefulWidget. In its State class, you need to remember whether the SkillJonglierten is shown or not. If it's shown, you need to show it in your build() function. Otherwise, you should not show it in your build() function. So, this becomes: https://codepen.io/gazialankus/pen/rNxgBgx
class _MyHomePageState extends State<MyHomePage> {
bool isShown;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Show'),
onPressed: () {
setState(() {
isShown = true;
});
}
),
if (isShown == true) SkillJonglieren()
],
),
),
);
}
}

Flutter ListView/ListTiles are not rendering on the page

I am new to Flutter/Dart but I am trying to incorporate a ListView into a project that I am working on but I cannot get it to work. I have tried it at least 12 different ways and it is still not working. I know the issue has something to do with the way I am sizing (or not sizing) the ListView/ListTiles. I have tried adding heights to SizedBoxes, heights to Containers, widths to columns, Expanded, Flexible etc. Nothing has worked. I am not posting the error messages because with all of the solutions I have tried they have all essentially been related to either the height or width of the ListView/ListTile exceeding the limitations of the screen. Below are the latest code snippet I have tried. I know there are several topics on SO that have addressed this but I have not been able to get any of them to work. My ListView should only contain 3 tiles so perhaps a ListView.Builder would be better. I am open to any suggestions or advice. Thanks in advance for the help!
class _HotelFormState extends State<HotelForm> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hotel Form'),
),
body: Column(
children: <Widget>[
SizedBox(height: 20.0),
Row(
children: <Widget>[
TripDates(),
],
),
SizedBox(height: 20.0),
Column(
children: <Widget>[
RoomCounts(),
],
),
],
),
);
}
}
RoomCounts Class
class RoomCounts extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Container(
height: 100.0,
child: new ListView(
children: new List.generate(3, (i) => new ListTileItem(title: "$i",)),
),
),
);
}
}
ListTile Class
class _ListTileItemState extends State<ListTileItem> {
//Some lists are here
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Flexible(
child: ListTile(
leading: Icon(widget.icons),
title: new Text(widget.title),
trailing: new Row(
children: <Widget>[
_itemCount != 0
? new IconButton(icon: new Icon(Icons.remove), onPressed: () => setState(() => _itemCount--),)
: new Container(),
new Text(_itemCount.toString()),
new IconButton(icon: new Icon(Icons.add), onPressed: () => setState(() => _itemCount++))
],
),
),
),
],
);
}
}
You can copy paste run full code below
You do not need Scaffold in RoomCounts
You do not need Column in _ListTileItemState
You need SizedBox in trailing of ListTile
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HotelForm(),
);
}
}
class HotelForm extends StatefulWidget {
#override
_HotelFormState createState() => _HotelFormState();
}
class _HotelFormState extends State<HotelForm> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hotel Form'),
),
body: Column(
children: <Widget>[
SizedBox(height: 20.0),
/*Row(
children: <Widget>[
TripDates(),
],
),*/
SizedBox(height: 20.0),
Column(
children: <Widget>[
RoomCounts(),
],
),
],
),
);
}
}
class RoomCounts extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 100.0,
child: ListView(
children: List.generate(
3,
(i) => ListTileItem(
title: "$i",
)),
),
);
}
}
class ListTileItem extends StatefulWidget {
final String title;
final IconData icons;
ListTileItem({this.title, this.icons});
#override
_ListTileItemState createState() => _ListTileItemState();
}
class _ListTileItemState extends State<ListTileItem> {
int _itemCount = 0;
#override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(widget.icons),
title: Text(widget.title),
trailing: SizedBox(
width: 200,
child: Row(
children: <Widget>[
_itemCount != 0
? IconButton(
icon: Icon(Icons.remove),
onPressed: () => setState(() => _itemCount--),
)
: Container(),
Text(_itemCount.toString()),
IconButton(
icon: Icon(Icons.add),
onPressed: () => setState(() => _itemCount++))
],
),
),
);
}
}

Flutter Scaffold.of(context).openDrawer() doesn't work

I want to open a drawer after pushing on the custom button in BottomMenu I have trouble with Scaffold.of(context).openDrawer(), it doesn't work. My BottomMenu is a separate widget class. As I understand, it doesn't work because it's a separate context. How can I get the right context? Or perhaps someone knows another solution.
Here my code reproducer:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(title: 'Flutter Drawer'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
bottomNavigationBar: BottomMenu(),
endDrawer: SizedBox(
width: double.infinity,
child: Drawer(
elevation: 16,
child: Container(
color: Colors.black,
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
ListTile(
title: Text('Some context here',
style: TextStyle(color: Colors.white))),
ListTile(
title: Text('Some context here',
style: TextStyle(color: Colors.white))),
],
),
),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Call Drawer form menu reproducer',
)
],
),
),
);
}
}
class BottomMenu extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Wrap(
alignment: WrapAlignment.center,
children: <Widget>[
Divider(color: Colors.black, height: 1),
Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
InkWell(
borderRadius: new BorderRadius.circular(20.0),
customBorder: Border.all(color: Colors.black),
child: Container(
padding: EdgeInsets.only(
left: 3, right: 6, bottom: 15, top: 11),
child: Row(
children: <Widget>[
Icon(Icons.menu),
Text('Show menu', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
],
),
),
onTap: () {
Scaffold.of(context).openDrawer();
},
),
],
),
),
],
),
);
}
}
In my case, this worked.
return Scaffold(
key: _scaffoldKey,
endDrawerEnableOpenDragGesture: false, // This!
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.white),
leading: IconButton(
icon: Icon(Icons.menu, size: 36),
onPressed: () => _scaffoldKey.currentState.openDrawer(), // And this!
),
),
drawer: DrawerHome(),
....
and _scaffoldKey must be initialized as,
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
under the class.
The problem is that you specified endDrawer on Scaffold yet you're calling Scaffold.of(context).openDrawer().
openDrawer() documentation states:
If the scaffold has a non-null Scaffold.drawer, this function will cause the drawer to begin its entrance animation.
Since your drawer is null, nothing happens.
In contrast, openEndDrawer() informs us:
If the scaffold has a non-null Scaffold.endDrawer, this function will cause the end side drawer to begin its entrance animation.
Since your endDrawer is not null you should use openEndDrawer() method. Alternatively, if you don't care which side the drawer slides in from, you can use drawer instead of endDrawer when building Scaffold.
My problem solved that instead of
Scaffold.of(context).openEndDrawer()
I give key to Scaffold and then I call by state like below
_scaffoldkey.currentState.openEndDrawer()
It solved my problem I hope It also works for you
Scaffold.of(context).openEndDrawer()
The Problem
This issue can occur when you do not use the correct BuildContext when calling Scaffold.of(context).openDrawer() (or openEndDrawer()).
Easiest Solution
Simply wrap whatever calls openDrawer() (or openEndDrawer()) with a Builder widget. This will give it a working context.
Minimal Working Example
// your build method
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: Builder(builder: (context) { // this uses the new context to open the drawer properly provided by the Builder
return FloatingActionButton(onPressed: (() => Scaffold.of(context).openDrawer()));
}),
drawer: const Drawer(
child: Text("MY DRAWER"),
),
);
}
Similar problem here. Clicked on button and nothing happened. The problem is I was using the context of the widget that instantiated Scaffold. Not the context of a child of Scaffold.
Here is how I solved it:
// body: Column(
// children: <Widget>[
// Row(
// children: <Widget>[
// IconButton(
// icon: Icon(Icons.filter_list),
// onPressed: () => Scaffold.of(context).openEndDrawer(), (wrong context)
// ),
// ],
// ),
// ],
// )
To:
body: Builder(
builder: (context) => Column(
children: <Widget>[
Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () => Scaffold.of(context).openEndDrawer(),
),
],
),
],
)),
),
Assign Drawer to drawer property in scaffold. Wrap your specific Widget/Button(where you want to open drawer on its click method) with Builder. Use below method on click property:
enter image description here
Scaffold.of(context).openDrawer();
If you have the appbar widget with an action button to launch the drawer and the drawer is never pushed please remember that you need to define after appbar: ... the endDrawer: YOURAppDrawerWIDGET(), or else using the Scaffold.of(context).openEndDrawer() will not work.
Scaffold(
appBar: AppBar(title: Text(_title)),
endDrawer: AppDrawer(), // <-- this is required or else it will not know what is opening
body: SingleChildScrollView(
///...