Interact with lower widget in Stack - flutter

I have a Stack that has a GoogleMap and an overlapping SingleChildScrollView, filled with Widgets.
However, the SCSV now block the map, and I can't interact with it? What I am trying to do, is a map as "upper" widget on the page and then the scrollview can be scrolled over the map.
Stack(children: <Widget>[
Container(
height: 400,
child: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition:
CameraPosition(target: _center, zoom: 13.0),
)),
SingleChildScrollView(
child: Padding(
padding: EdgeInsets.fromLTRB(0, 300, 0, 0),
child: Container(
decoration: BoxDecoration(color: Colors.white),
child: Column(children: _offers))))
])
Any idea how I can make this happen? Other suggestions than my existing layout are more than welcome, as long as the results is the same :)

I think you had the same problem as I solved in my application. I created a stack of two interactive widgets in my photo preview — pan/draw — and wanted to allow users to switch the interaction between them. As they are all full-screen widgets, I need to make the "top" one be sometimes transparent for the user's clicks, depends on a boolean flag (panEnable in my case).
I solved that with the IgnorePointer widget:
class PhotoPreviewScreen extends StatefulWidget {
_PhotoPreviewScreen createState() => _PhotoPreviewScreen();
}
class _PhotoPreviewScreen extends State<PhotoPreviewScreen> {
bool panEnable = true; // THAT IS THE enable/disable flag
final String imagePath = 'my_image.jpg'; // image to show and interact with
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
InteractiveViewer( // This is the "lower widget"
panEnabled: panEnable, // THAT IS THE enable/disable flag
scaleEnabled: panEnable, // THAT IS THE enable/disable flag
boundaryMargin: EdgeInsets.all(double.infinity),
minScale: 1,
maxScale: 16,
child: Image.file(
File(imagePath), // imagePath — local path to an image in Preview
),
),
Center(
child: Stack(
children: <Widget>[
IgnorePointer(
ignoring: panEnable, // THAT IS THE enable/disable flag
child:
SomeFullScreenActionWidget( // AN UPPER ACTION
onPressed: () => <SOME ACTION>,
), // SomeFullScreenActionWidget
), // IgnorePointer
// <here might be some "passive" widgets that are not use pointers
], // children of Stack widget
), // Stack
), // Center
IconButton( // wrap it in Align/Positioned or any other widget to arrange the widgets switch button on the screen
iconSize: 33.0,
color: Colors.white,
icon: Icon(!panEnable? CupertinoIcons.pencil_outline: CupertinoIcons.pencil_slash), // I use some stylish icons, you may choose standard
onPressed: () {
setState(() {
panEnable = !panEnable; // HERE IS THE SWITCH enable/disable flag
}); // SetState
}, // onPressed
), // IconButton
], // children of Stack
), // Stack
); // Scaffold
That is important to mention that I use a StatefulWidget class to use setState(). In the case of a StatelessWidget class, it would not work.

Related

Single Child Scoll View doesn't scroll up screens when soft keyboard appears in Flutter Webview Plugin

Here is my main.dart
class _MyHomePageState extends State {
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: const WebviewController(),
),
),
);
}
Does anyone who know this answer???
plz.. tell me your solutions...
I used Single child scroll view to scoll up my screens when soft keyboard appears in android..
Also use Adjust Resizing but doesn't work.
IOS device has no problem but only in android device...
ps. If you needed, I'll attach webview_controller.dart too..
I also cant make it scrollable using SingleChildScrollView only but I found a workaround to do that. I kept a flag when keyboard opens and modified my widgets accordingly. Here is the example.
class _MyHomePageState extends State {
bool _keyboardOpen = false;
#override
void initState() {
super.initState();
FocusManager.instance.primaryFocus?.unfocus();
}
#override
Widget build(BuildContext context) {
_keyboardOpen = MediaQuery.of(context).viewInsets.bottom == 0;
return Scaffold(
resizeToAvoidBottomInset: true,
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Visibility(
visible: _keyboardOpen,
child: const SizedBox(
height: 10,
),
),
),
),
);
}
Here you can make non-visible sizedBox when keyboard opens, you can also decrease the text's size when keyboard appears like this.
Text('your text', textAlign: TextAlign.center,
style: TextStyle(fontSize: (_keyboardOpen)? 22 : 9, fontWeight:
FontWeight.w500)
),
Let me know if this helps.

Flutter Card child content height is larger than its parent

I'm trying to use a GridView to handle displays for multiple Card, each Card contains of an Image. Unfortunately it turns out that the Image is taking a larger height than its parent (see attached picture for the details).
I'm pretty new to Flutter layout so any ideas why this is happening and how I can resolve this? I want the layout to be something like this:
Display 2 cards on each line.
The Card width or height should not be fixed.
The Image height should be scaled according to its width.
class SquadSelectionScreen extends StatelessWidget {
final List<Team> teams;
const SquadSelectionScreen({super.key, required this.teams});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Squads'),
),
body: GridView.count(
crossAxisSpacing: 10,
crossAxisCount: 2,
padding: const EdgeInsets.all(16),
children: teams
.map(
(team) => SquadView(team: team),
)
.toList(),
),
);
}
}
class SquadView extends StatelessWidget {
final Team team;
const SquadView({super.key, required this.team});
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
context.push('/squads/${team.code}');
},
child: Card(
elevation: 1,
child: Column(
children: [
Image(
image: NetworkImage(team.imageUrl),
),
const SizedBox(
height: 8,
),
Center(
child: Text(team.name),
),
],
),
),
);
}
}
Using GridView.count has a very visible drawback, namely the size of the aspect ratio of the grid will always be one (1:1 or Square) and can't be changed.
So if you look at the code above, you can't set an image with the same aspect ratio because the text will sink.
The first suggestion for me if you still want to use GridView.count is
Wrapping your Image with AspectRatio that has value higher than one (example set Ratio to 4/3, 5/3, 16/9, or landscape looks). Note: 4/3 = is higher than 1, 16/9 = is higher than 1, etc..
Then wrap the Text Widget with Expanded()
Example code:
class SquadView extends StatelessWidget {
final Team team;
const SquadView({super.key, required this.team});
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {},
child: Card(
elevation: 1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
AspectRatio(
aspectRatio: 4/3, // you can set the value to 16/9 or anything that result is higher than one
child: Image(
image: NetworkImage(team.imageUrl),
fit: BoxFit.cover, // set How the image looks to Fit
),
),
const SizedBox(
height: 8,
),
Expanded(
child: Center(
child: Text(team.name, overflow: TextOverflow.ellipsis),
),
),
],
),
),
),
);
}
}
I suggest you try GridView.builder or another GridView. You can look at the documentation here
or this third package this will be good for to try flutter_staggered_grid_view. The flutter_staggered_grid_view is more flexible to create GridView with various size.

How to fix a button at bottom of a single child scrollview with a list

I have a SingleChildScrollView and inside it I have a list with some cards, that you can remove ou add more. I need to fix an add button at the bottom of the screen, when the card list is not scrollable yet, but when the card list increase size and the scrollview is able to scroll now (to see all the content), the button must follow the list and not keep fixed at the bottom anymore.
For now, what I did to solve this, was check the scroll view every time that a card is added ou removed, if I checked that the screen is now scrollable or not scrollable I change some properties of my build widget:
SingleChildScrollView(
controller: _scrollController,
physics: AlwaysScrollableScrollPhysics(),
child: Container(
height: isNotScrollable
? _pageSize - (_appBarSize + _notifySize)
: null,
padding: const EdgeInsets.symmetric(
horizontal: Constraints.paddingNormal),
child: Column(
.....
and after the list render I create the button like this
isNotScrollable
? Expanded(
child: Container(),
)
: Container(),
CVButton(
color: Palette.white,
Basically, my idea is: if the screen is not scrollable yet (the list content fits in the screen size) I will set a height to the container inside scrollview and add a Expanded() widget before the add button (so the button will stay in the bottom of the container), but if the screen is scrollable (the list content not fits inside the screen size) so I remove the container height and the Expanded widget, then the button will follow the list now as normally.
I don't know if this is the better way to deal with that, I want to know if there is some way to do this without this 'dinamic' way that I am doing, only with fixed widgets and not changing the widget according to the state of the scrollview.
An example when the list becomes scrollable and the button will keep at list bottom
Here the list is not scrollable yet but the button must be at the screen bottom and not list bottom
(I dont wanna use bottomNavBar)
Anyone has any idea how I can solve this?
I have a solution for this. check the code bellow. I added some buttons to add or remove cards. The main trick is to use constraints like minHeight.
import 'package:flutter/material.dart';
class BottomButton extends StatefulWidget {
#override
_BottomButtonState createState() => _BottomButtonState();
}
class _BottomButtonState extends State<BottomButton> {
List<Widget> cards = [];
#override
Widget build(BuildContext context) {
var appBar2 = AppBar(
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () {
_addCard();
}),
IconButton(
icon: Icon(Icons.remove),
onPressed: () {
_removeCard();
}),
],
);
return Scaffold(
appBar: appBar2,
body: Container(
height: MediaQuery.of(context).size.height -
(MediaQuery.of(context).padding.top + appBar2.preferredSize.height),
alignment: Alignment.topCenter,
child: ListView(
primary: true,
children: [
Container(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height -
(MediaQuery.of(context).padding.top +
appBar2.preferredSize.height),
),
alignment: Alignment.topCenter,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height -
(MediaQuery.of(context).padding.top +
appBar2.preferredSize.height +
50),
),
alignment: Alignment.topCenter,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: cards,
),
),
ElevatedButton(
child: Text('this is a button'),
onPressed: () {},
),
],
),
)
],
),
),
);
}
void _addCard() {
Widget card = Card(
child: Container(
height: 100,
color: Colors.red,
padding: EdgeInsets.all(2),
),
);
setState(() {
cards.add(card);
});
}
void _removeCard() {
setState(() {
cards.removeLast();
});
}
}

TextOverFlow Flutter

I have a certain Text widget , when it overflows I have 3 options. Either fade ,visible, ellipsis or clip. But I don't want to choose between them . I want if a text has overflow then don't show the text.
Edit :
I'm working on a code clone to this design
Assuming that the textStyle is unknown.
How could I achieve that?
Code:
class SwipeNavigationBar extends StatefulWidget {
final Widget child;
SwipeNavigationBar({this.child});
#override
_SwipeNavigationBarState createState() => _SwipeNavigationBarState();
}
class _SwipeNavigationBarState extends State<SwipeNavigationBar> {
#override
Widget build(BuildContext context) {
return Consumer<Controller>(
builder: (_, _bloc, __) {
return SafeArea(
child: AnimatedContainer(
duration: Duration(seconds: 01),
color: Colors.white,
curve: Curves.easeIn,
height: !_bloc.x ? 50 : 200,
child: Row(
children: [
Column(
verticalDirection: VerticalDirection.up,
children: [
Expanded(child: Icon(Icons.dashboard)),
Expanded(
child: RotatedBox(
quarterTurns: -45,
child: Text(
'data',
softWrap: false,
style: TextStyle(
textBaseline: TextBaseline.alphabetic
),
),
),
),
],
)
],
),
),
);
},
);
}
}
To mimic the design you might want to look into using the Stack widget. However, to answer your question, you'd want to set softWrap to false.
Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 100,
child: Text(
'Some text we want to overflow',
softWrap: false,
),
),
)
softWrap is really the key here. Although, I added the Align and SizedBox widgets to allow this to be used anywhere, regardless of what parent widget you are using (since some widgets set tight constraints on their children and will override their children's size preference).
CodePen Example
Edit: 5/6/2020
With the release of Flutter v1.17 you now have access to a new Widget called NavigationRail which may help you with the design you're looking for.
Use ternary operator to check the length of the text that you are passing to the Text widget and based on that pass the text itself or an empty string.
String yourText;
int desiredLengthToShow = 10; //Change this according to you.
...
Text(
child: yourText.length > desiredLengthToShow ? "" : yourText,
);

Color of a widget inside a Stack is always slightly transparent

I display a custom-made bottom app bar in a Stack because of keyboard padding reasons. The custom widget is fully opaque as it should be until it's a child of a Stack in which case, the content behind it starts to be visible since the color's opacity somehow changes.
As you can see, it's only the "main" color that's transparent. Icons remain opaque.
This is the build method of my custom BottomBar widget which is then just regularly put into a Stack. I have tried using a Material and even a simple Container in place of the BottomAppBar widget but the results are the same.
#override
Widget build(BuildContext context) {
return BottomAppBar(
color: Colors.blue.withOpacity(1),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(MdiIcons.plusBoxOutline),
onPressed: () {},
),
Text('Edited 11:57'),
IconButton(
icon: Icon(MdiIcons.dotsVertical),
onPressed: () {},
),
],
),
);
}
Can you interact with the BottomAppBar ? It looks like an order problem. Try to put the BottomAppBar as last in the Stack children.
Note that BottomAppBar doesn't have a constant size, if you did not add it to Scaffold bottomNavigationBar named parameter has a size if this is not null. Below is peace of code in Scaffold dart file:
double bottomNavigationBarTop;
if (hasChild(_ScaffoldSlot.bottomNavigationBar)) {
final double bottomNavigationBarHeight = layoutChild(_ScaffoldSlot.bottomNavigationBar, fullWidthConstraints).height;
bottomWidgetsHeight += bottomNavigationBarHeight;
bottomNavigationBarTop = math.max(0.0, bottom - bottomWidgetsHeight);
positionChild(_ScaffoldSlot.bottomNavigationBar, Offset(0.0, bottomNavigationBarTop));
}
You can even develop your own Widget without BottomAppBar but if you want things like centerDocked and things like circular notched, you will have to do more stuff (anyway you have flexibility to custom design the way you want).
Here is a simple example to do that(one way to do that):
import 'package:flutter/material.dart';
class CustomBottomBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 50),
color: Colors.greenAccent, // if you want this color under bottom bar add the margin to list view
child: ListView.builder(
itemCount: 100,
itemBuilder: (_, int index) => Text("Text $index"),
),
),
Positioned(
bottom: 0,
child: Container(
color: Colors.amber.withOpacity(.5),
width: MediaQuery.of(context).size.width,
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(4, (int index) => Text("Text $index")), // you can make these clickable by wrapping with InkWell or any gesture widget
),
),
),
],
),
);
}
}