PageView is causing unbounded height error - flutter

class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void openModal() {
showModalBottomSheet<dynamic>(
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20), topRight: Radius.circular(20))),
context: context,
builder: (_) {
return Wrap(
children: [
ModalSheetContent(),
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: FloatingActionButton(
onPressed: openModal,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
),
);
}
}
class ModalSheetContent extends StatefulWidget {
const ModalSheetContent({Key? key}) : super(key: key);
#override
_ModalSheetContentState createState() => _ModalSheetContentState();
}
class _ModalSheetContentState extends State<ModalSheetContent> {
final PageController pageController = PageController(
initialPage: 0,
keepPage: true,
);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: PageView(
controller: pageController,
children: [imageVideo(context), Pricing()],
),
);
}
I want the modalSheet to take the dynamic height according to its content and not full screen. I tried wrapping PageView with Column and Expanded but it didn't work.
Error:
Viewports expand in the cross axis to fill their container and constrain their children to match their extent in the cross axis. In this case, a horizontal viewport was given an unlimited amount of vertical space in which to expand.The relevant error-causing widget was PageView

PageView must have a height in flutter, usually it takes the biggest part of the screen but in your case you are showing it inside a bottomsheet, I assume that your bottomsheet should be almost full screen, so try to give it a height
final size = MediaQuery.of(context).size;
……
SizedBox(
height: size.height - 100;
child: PageView(…),
)

If you want that your bottom sheet takes dynamic height according to its context, simple wrap your widget into column widget and add this attribute.
Column(
mainAxisSize:MainAxisSize.min,
children:[...]
)

Related

How to give height based on available height for customWidget in flutter

I have created a customWidget for showing data named CustomShowDataWidget, and this widget is used in many screens but with different available heights.. so how to apply height based on available screen height...
here is my custom widget
class ShowTransactionWidget extends StatelessWidget {
ShowTransactionWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
height: 500,//-< I have given fixed height what I dont want...
color: Colors.grey.shade200,
child: Column(
children: [
Align(
child: Text('Recent Transactions',style: TextStyle(
color: Colors.blue,
fontSize: 20
),),
alignment: Alignment.centerLeft,
),
Expanded(
child: ListView.builder(
itemCount: 100,
itemBuilder: (context,index){
return ListTile(
title: Text('Hello $index'),
);
}),
)
],),
);
}
}
here is the one of the screen where I am using this widget
class NextScreen extends StatelessWidget {
const NextScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(children: [
Container(
color: Colors.blue,
height: MediaQuery.of(context).size.height*0.70,
),
ShowTransactionWidget(),
],),
);
}
}
Try the following code:
class ShowTransactionWidget extends StatelessWidget {
const ShowTransactionWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
color: Colors.grey.shade200,
child: Column(
children: [
const Align(
alignment: Alignment.centerLeft,
child: Text(
"Recent Transactions",
style: TextStyle(color: Colors.blue, fontSize: 20),
),
),
Expanded(
child: ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return const ListTile(
title: Text("Hello"),
);
},
),
)
],
),
),
);
}
}
ShowTransactionWidget(),
You want your widget ShowTransactionWidget to have a height which is relative to the available height given to it.
Okay, first, you must learn that in flutter, Constraints go down. Sizes go up. Parent sets position. So if your widget can be a part of other widgets, don't always expect it can take the size you want it to take.
After you learn that, you have multiple options:
Use LayoutBuilder widget to wrap your Container. This widget will give you the available constraints to your widget, and you can use the max height to determine the height of your widget.
class ShowTransactionWidget extends StatelessWidget {
ShowTransactionWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final maxHeight = constraints.maxHeight;
return Container(
height: maxHeight/2,
...
Use the MediaQuery to get the size inside your ShowTransactionWidget, but you may want more information than the size of the screen as your widget might not be allowed to have the whole size of the screen, so this option might not fit all cases.
class ShowTransactionWidget extends StatelessWidget {
ShowTransactionWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size; // The size of the media in logical pixels (e.g, the size of the screen).
return Container(
height: size.height/2,
...
Pass the max height to your ShowTransactionWidget in its constructor, and then define the height of the Container relative to that height
class ShowTransactionWidget extends StatelessWidget {
final double height;
ShowTransactionWidget({Key? key, required this.height}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
height: height, // passed from parent, this method of passing the height is useful when your child's height is always determined by the parent's height
...
Use more complex widgets like CustomSingleChildLayout.

Flutter Draggable ListView feedback scroll position

While using Flutter, I am trying to put a ListView inside of a Draggable. Using PageStorageKey I was able to preserve the scroll position before and after dragging with this minimal example:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget list = ListView(
key: const PageStorageKey("id"),
controller: ScrollController(),
children: const [
Text("1"),
Text("2"),
Text("3"),
Text("4"),
Text("5"),
Text("6"),
Text("7"),
Text("8"),
Text("9"),
Text("10"),
Text("11")
],
);
double x = 300;
double y = 100;
#override
Widget build(BuildContext context) {
final listWidget = SizedBox(
width: x,
height: y,
child: Card(
elevation: 0,
shape: const RoundedRectangleBorder(
side: BorderSide(color: Colors.black),
borderRadius: BorderRadius.all(Radius.circular(12))),
child: list,
));
return Scaffold(
body: Draggable(
child: listWidget,
feedback: listWidget,
childWhenDragging: const SizedBox()));
}
}
However, once drag starts the feedback widget does not have the same scroll position. Is there a way to preserve the scroll position during drag?

Flutter: Custom widget with PageView physics and interactions

I need to make a screen on which the interaction is similar to PageView, but with something very different on the screen. I don't want to deal with separate pages. I only need the current scroll position to place my widgets as I desire.
I succeeded in achieving this by placing PageView with empty pages at the bottom of the Stack and placing my widgets inside AnimatedBuilder above PageView. The only problem is that my widgets have their own GestureDetectors and that's why PageView doesn't scroll when I start scrolling gestures over my widget.
Here is a sample. I need the same behavior but without scroll problems when I start dragging over my top widgets.
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _pageController = PageController(viewportFraction: 0.7, initialPage: 5);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: [
PageView.builder(
controller: _pageController,
itemBuilder: (context, index) => Container(),
itemCount: 10,
),
for (double angleOffset in [0.25, 0.5, 0.75, 1])
AnimatedBuilder(
animation: _pageController,
builder: (context, child) {
return Center(
child: Transform.rotate(
angle: (_pageController.page ?? 5) + pi * angleOffset,
child: Transform.translate(
offset: const Offset(0, 150),
child: const ColorContainer(),
),
),
);
},
),
],
),
);
}
}
class ColorContainer extends StatefulWidget {
const ColorContainer({Key? key}) : super(key: key);
#override
State<ColorContainer> createState() => _ColorContainerState();
}
class _ColorContainerState extends State<ColorContainer> {
double hue = 30;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => hue = (hue + 80.0) % 360),
child: Container(
width: 100,
height: 100,
decoration: ShapeDecoration(
shape: const CircleBorder(),
color: HSLColor.fromAHSL(1, hue, 1, 0.5).toColor(),
),
),
);
}
}

where does FocusScope widget exist in the widget tree?

Where does the FocusScope widget create in the tree and we pass every context in it and it can request to any focus nodes. When we pass context to FocusScope it will start looking above the context and we never used the FocusScope widget in the code in the hierarchy where does it create and how does it resolves in the case of scaffold if we pass context that is above in the tree then it throws an exception then we use builder type of thing but in FocusScope why it doesn't throw an error?
Here is the example for the FocusScope
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// A demonstration pane.
///
/// This is just a separate widget to simplify the example.
class Pane extends StatelessWidget {
const Pane({
Key? key,
required this.focusNode,
this.onPressed,
required this.backgroundColor,
required this.icon,
this.child,
}) : super(key: key);
final FocusNode focusNode;
final VoidCallback? onPressed;
final Color backgroundColor;
final Widget icon;
final Widget? child;
#override
Widget build(BuildContext context) {
return Material(
color: backgroundColor,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Center(
child: child,
),
Align(
alignment: Alignment.topLeft,
child: IconButton(
autofocus: true,
focusNode: focusNode,
onPressed: onPressed,
icon: icon,
),
),
],
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool backdropIsVisible = false;
FocusNode backdropNode = FocusNode(debugLabel: 'Close Backdrop Button');
FocusNode foregroundNode = FocusNode(debugLabel: 'Option Button');
#override
void dispose() {
super.dispose();
backdropNode.dispose();
foregroundNode.dispose();
}
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
final Size stackSize = constraints.biggest;
return Stack(
fit: StackFit.expand,
// The backdrop is behind the front widget in the Stack, but the widgets
// would still be active and traversable without the FocusScope.
children: <Widget>[
// TRY THIS: Try removing this FocusScope entirely to see how it affects
// the behavior. Without this FocusScope, the "ANOTHER BUTTON TO FOCUS"
// button, and the IconButton in the backdrop Pane would be focusable
// even when the backdrop wasn't visible.
FocusScope(
// TRY THIS: Try commenting out this line. Notice that the focus
// starts on the backdrop and is stuck there? It seems like the app is
// non-responsive, but it actually isn't. This line makes sure that
// this focus scope and its children can't be focused when they're not
// visible. It might help to make the background color of the
// foreground pane semi-transparent to see it clearly.
canRequestFocus: backdropIsVisible,
child: Pane(
icon: const Icon(Icons.close),
focusNode: backdropNode,
backgroundColor: Colors.lightBlue,
onPressed: () => setState(() => backdropIsVisible = false),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// This button would be not visible, but still focusable from
// the foreground pane without the FocusScope.
ElevatedButton(
onPressed: () => debugPrint('You pressed the other button!'),
child: const Text('ANOTHER BUTTON TO FOCUS'),
),
DefaultTextStyle(
style: Theme.of(context).textTheme.headline2!,
child: const Text('BACKDROP')),
],
),
),
),
AnimatedPositioned(
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 300),
top: backdropIsVisible ? stackSize.height * 0.9 : 0.0,
width: stackSize.width,
height: stackSize.height,
onEnd: () {
if (backdropIsVisible) {
backdropNode.requestFocus();
} else {
foregroundNode.requestFocus();
}
},
child: Pane(
icon: const Icon(Icons.menu),
focusNode: foregroundNode,
// TRY THIS: Try changing this to Colors.green.withOpacity(0.8) to see for
// yourself that the hidden components do/don't get focus.
backgroundColor: Colors.green,
onPressed: backdropIsVisible
? null
: () => setState(() => backdropIsVisible = true),
child: DefaultTextStyle(
style: Theme.of(context).textTheme.headline2!,
child: const Text('FOREGROUND')),
),
),
],
);
}
#override
Widget build(BuildContext context) {
// Use a LayoutBuilder so that we can base the size of the stack on the size
// of its parent.
return LayoutBuilder(builder: _buildStack);
}
}

Clip Stack children

How to clip Stack children within it's size.
In this image there are 3 grid-Items using orange color and every item using InkWell to use hover-Method to Align on Stack. While hover:false the Pop PoP Widget won't be visible to the UI. With align property it works, but as you can see the Right Top GridItem's item:2 pop POp widget is visible outside the Stack<Griditem> and I want to make it invisible outside the stack. I've tested using clipBehavior: with every Clip enums.
I want to hide the Pop POp widget while it is outside the Stack and yes I need this pop-up effect.
For Flutter web and I'm using Flutter V2.5.2
Current Layout with Issue
Full Code to reproduce the issue
import 'package:flutter/material.dart';
void main() => runApp(
const MaterialApp(
home: Appp(),
),
);
class Appp extends StatelessWidget {
const Appp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const BodyX();
}
}
class BodyX extends StatelessWidget {
const BodyX({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(body: LayoutBuilder(
builder: (context, constraints) {
return GridView.count(
crossAxisCount: 2,
children: [
...List.generate(
3,
(index) => GridItem(
key: UniqueKey(),
maxWidth: constraints.maxWidth / 2,
),
),
],
);
},
));
}
}
class GridItem extends StatefulWidget {
const GridItem({
Key? key,
required this.maxWidth,
}) : super(key: key);
final double maxWidth;
#override
State<GridItem> createState() => _AppXState();
}
class _AppXState extends State<GridItem> {
bool _isHovered = false;
#override
Widget build(BuildContext context) {
print("ItemWidth : ${widget.maxWidth}");
return SizedBox(
//though it wont effect here,
// just finding the size of Grid because it will 1x1
width: widget.maxWidth,
height: widget.maxWidth,
child: InkWell(
onTap: () {},
hoverColor: Colors.black,
onHover: (value) {
setState(() {
_isHovered = value;
});
},
child: Stack(
clipBehavior: Clip.antiAliasWithSaveLayer,
children: [
Container(
color: Colors.deepOrange.withOpacity(.2),
),
AnimatedAlign(
alignment: Alignment(0, _isHovered ? .7 : 2),
child: Container(
padding: const EdgeInsets.all(22),
color: Colors.greenAccent,
child: const Text(
"Pop POp",
),
),
duration: const Duration(
milliseconds: 200,
),
)
],
),
),
);
}
}
If you don't want a Widget to draw beyond its layout size, you can use ClipRect to clip it.
In your case, you can wrap ClipRect on your Stack, like so:
ClipRect(
child: Stack(
children: ...
),
)
Further more, you can use ClipRRect to clip a rounded rectangle shape (circular border) or ClipPath to clip a custom shape, like a triangle. You can read more about these widgets in the official docs.