Flutter How to change container height based on the ListView's item height? - flutter

Hello I have a Scaffold wrapped with SingleChildScrollView and child is Column.
Inside Column; Container, TabBar and TabBarView.
First Container is just there for black space.
Container(
color: Colors.black,
height: 300,
),
The second widget of Column which mean TabBar:
(I know we can use it in AppBar but now it is what it is.)
const TabBar(
labelColor: Colors.red,
tabs: [
Tab(text: "Tab1"),
Tab(text: "Tab2"),
Tab(text: "Tab3"),
],
),
Last Column widget is TabBarView. It wrapped by Container that has 300 height.
Container(
height: 300, // here is problem
color: Colors.amber,
child: TabBarView(
children: [
buildContainer(200, Colors.red, 2),
buildContainer(100, Colors.red, 2),
buildContainer(150, Colors.red, 3),
],
),
),
and also this is buildContainer method;
buildContainer(double height, Color color, int count) => ListView.builder(
shrinkWrap: true,
itemCount: count,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: height,
color: color,
child: Center(
child: Text("my height ${height.toString()}"),
),
),
);
});
Here is my question. I have 3 tabs and I have three ListViewBuilder.Each one has own child count. But all of them height limited to 300 because of their parent that is Container. I want to set Tab's height dynamicly with each ListViewBuilder's item count.
How can I do that ? I accept dynamic height without child scrolling. I mean, I can scroll whole page for reach the last child. In example, Instagram profile tab. If I have 30 photo, height is phone height. But I have 300 photo, it scrolling all the way down. But also, I don't want to understand pagenation for this one. I am not going to do Instagram. I just want to that, If I create 5 container, okey show me your max height. If I create 1 container, show me just that without scrolling.

I added a dynamic height calculation depending on the number of objects in the ListView.builder. The formula is not perfect, you can add to it, but the point is to subtract the AppBar height and padding to get a clean screen area that the widget completely occupies.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: mainWidget(),
);
}
Widget mainWidget() {
AppBar appBar = AppBar(
toolbarHeight: 56, //You can manually set the AppBar height
title: const Text("App bar"),
);
print(appBar.preferredSize); // Or you can save this value and use it later, it will not be fixed, but will depend on the screen size
return Scaffold(
appBar: appBar,
body: HelpSO(color: Colors.red, count: 5),
);
}
}
class HelpSO extends StatelessWidget {
late double height;
Color color;
int count;
HelpSO({Key? key, required this.color, required this.count})
: super(key: key);
#override
Widget build(BuildContext context) {
// height = (deviceHeight / itemCount) - padding (top + bottom) - appbar.prefferedSize.height / 2;
height = (MediaQuery.of(context).size.height / count) - 16.0 - 56 / 2;
return ListView.builder(
shrinkWrap: true,
itemCount: count,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0), // Subtract this value
child: Container(
height: height,
color: color,
child: Center(
child: Text("My height ${height.toString()}"),
),
),
);
});
}
}

I'm new at flutter(2 m).I just come accros with this problem.
My solution was juste wrap first or second(base on your logic) container with a SingleChildScrollView
Hope it will be helpful

Related

Complete Dialog is not scrollable but just the listview inside it is scrollable

I am trying to create a modal which contains some details and is scrollable. The modal contains a ListView which is scrollable, but the complete modal is not.
I have tried adding the following options to the ListView, but it didn't help
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics()
The minimum reproducible code is
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(
// Application name
title: 'Flutter Stateful Clicker Counter',
theme: ThemeData(
// Application theme data, you can set the colors for the application as
// you want
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Clicker Counter Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
#override
_MyHomePageState createState() => _MyHomePageState();
}
_buildTheDescriptionWizard() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: Text(
"Paneer is a fresh cheese used in a wide variety of Indian recipes, made by heating and then curdling milk using acid. It's very mild and milky in flavor, white in color, and its texture is soft, spongy, and squeaky. This texture helps it to absorb the flavors of sauces or marinades. It can be made from cow's milk or buffalo milk, either pasteurized or raw, and can be made from whole, skim or reduced-fat milk. ",
textAlign: TextAlign.left,
style: TextStyle(fontSize: 15, height: 1.5, color: Colors.black))),
);
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)),
elevation: 16,
child: createModal());
}
createModal() {
return Container(
height: 600,
width: 800,
child: Container(
color: Colors.white,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(children: <Widget>[
Container(
height: 300,
child: ListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
Container(
width: 300,
color: Colors.white,
// child:
// DropShadow(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
'https://picsum.photos/250?image=9',
height: 250,
width: 250),
),
// )
),
Container(
width: 450, height: 300, color: Colors.black)
],
)),
Divider(),
_buildTheDescriptionWizard()
]))));
}
}
Can someone please help me figure out how I can make the complete modal scrollable ?
remove SingleChildScrollView and Column just use ListView and don't use shrinkWrap: true and physics: const NeverScrollableScrollPhysics()

Flutter - Draggable AND Scaling Widgets

So for this application (Windows, Web) I have 2 requirements:
User can drag around widgets on the screen (drag and drop) to any location.
The app must scale to screen/window size
For (1) I used this answer.
For (2) I used this solution.
As mentioned in the code comment below I can't have both:
If I set logicWidth and logicHeight dynamically depending on the window size, the dragging works fine but the draggable widgets won't scale but instead stay the same size regardless of the window size.
If I set logicWidth and logicHeight to a constant value (the value of the current cleanHeight ) the dragging will be messed up for other screen sizes but then the draggable widgets will scale correctly with the window size.
In other words: for the dragging to work nicely these values need to be matching the window size at any time. But by changing these values I ruin the scaling I need.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:matrix_gesture_detector/matrix_gesture_detector.dart';
//containing widgets to drag around
const List<Widget> draggableWidgets = [
DraggableWidget(
draggableWidget: CircleAvatar(
backgroundColor: Colors.green,
radius: 32,
)),
DraggableWidget(
draggableWidget: CircleAvatar(
backgroundColor: Colors.red,
radius: 24,
)),
];
class FrontPageWidget extends ConsumerWidget {
const FrontPageWidget({Key? key}) : super(key: key);
static const routeName = '/frontPage';
#override
Widget build(BuildContext context, WidgetRef ref) {
//screen height and padding
final height = MediaQuery.of(context).size.height;
final padding = MediaQuery.of(context).viewPadding;
// Height (without status and toolbar)
final cleanHeight = height - padding.top - kToolbarHeight;
//either make those values dynamic (cleanHeight updates depending on screen size / window size) OR constant (961px is the cleanHeight on full screen)
//if values are dynamic => the draggable widgets not scaling to screen size BUT dragging works fine
//if values are constant => the draggable widgets do scale to screen size BUT dragging is messed
final logicWidth = cleanHeight; //961
final logicHeight = cleanHeight; //961
return Scaffold(
appBar: AppBar(
title: const Text('Main Page'),
),
body: SizedBox.expand(
child: FittedBox(
fit: BoxFit.contain,
alignment: Alignment.center,
child: Container(
color: Colors.grey,
width: logicWidth,
height: logicHeight,
child: Stack(
children: draggableWidgets,
),
))),
);
}
}
class DraggableWidget extends StatelessWidget {
final Widget draggableWidget;
const DraggableWidget({Key? key, required this.draggableWidget})
: super(key: key);
#override
Widget build(BuildContext context) {
final ValueNotifier<Matrix4> notifier = ValueNotifier(Matrix4.identity());
return Center(
child: MatrixGestureDetector(
onMatrixUpdate: (m, tm, sm, rm) {
notifier.value = m;
},
child: AnimatedBuilder(
animation: notifier,
builder: (ctx, child) {
return Transform(
transform: notifier.value,
child: Center(
child: Stack(
children: [draggableWidget],
),
),
);
},
),
),
);
}
}
One way of doing it is wrapping the draggableWidget in a Transform widget and set the scale factor in relation to the dimensions:
child: AnimatedBuilder(
animation: notifier,
builder: (ctx, child) {
final height = MediaQuery.of(context).size.height;
return Transform(
transform: notifier.value,
child: Center(
child: Stack(
children: [
Transform.scale(
scale: height / 1000,
child: draggableWidget)
],
),
),
);
},
),
I had a similar issue, instead of getting the height from the MediaQuery get it from the LayoutBuilder, I noticed it is working much better when resizing the window.
body: LayoutBuilder(
builder: (context, constraints) {
return SizedBox.expand(
child: FittedBox(
fit: BoxFit.contain,
alignment: Alignment.center,
child: Container(
color: Colors.grey,
width: constraints.maxWidth,
height: constraints.maxHeight,
child: Stack(
children: draggableWidgets,
),
)
)
);
}
);
Another way of achieving this:
To drag around widgets on the screen (drag and drop) to any location.
Draggable Widget
Check Flutter Draggable class
And to scale screen/window size.
Relative Scale
FlutterScreenUtil

How to keep wrap content height for flutter listview with horizontal scrolling? [duplicate]

This question already has answers here:
Horizontal ListView flutter WITHOUT explicit height
(2 answers)
Closed 6 months ago.
I made list view with horizontal scrolling. And it is not working with out setting an height.
My code is
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text(""),
),
body: Column(
children: [
Container(
height: 100,
color: Colors.red,
child: ListView.builder(
itemCount: 5,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Card(
color: Colors.amberAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("$index"),
),
);
}),
),
const Expanded(child: Text("Some other views")),
],
),
),
);
}
}
How can I leave the listview height as wrap content?
That's one of the things you will have to live with. If there is a list of 1 million items and each one is wrap_content and somehow one of the list item is going to be 1000px in height, flutter has no way of knowing this as it only lays out items which are going to be visible (or has potential to be visible on user interaction immediately). It doesn't build all the million items at once so we need to provide some height.
This is true for vertical lists as well. We usually don't pay attention as most apps in portrait mode have not much width so it matches parent's width without any issue.

Flutter: ListView - Green overlay instead of arrows in widget inspector

I'm creating a ListView with a builder function. I use the widget inspector to assess the any issues with the layout of the widgets.
Usually, the listView shows downwards green arrows as shown here:
[ListView layout][1]
However, in my current app, whenever I create a listView, it shows this green overlay on the listView. This creates artefacts with Image widget nested in stack; the images flicker when scrolling. [artefact layout][2]
This layout does look like it will take the space of 'drawer' in scaffold however, this page does not have a drawer, although all the other pages do.
Please find the code below for your reference.
const BlogListPage({Key? key}) : super(key: key);
#override
State<BlogListPage> createState() => _BlogListPageState();
}
class _BlogListPageState extends State<BlogListPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: ASAppBar(
title: const Text('Blogs'),
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Container(
margin:
const EdgeInsets.symmetric(vertical: 16.0, horizontal: 24.0),
color: Colors.amber,
child: const SizedBox(
height: 100,
width: double.infinity,
),
);
},
));
}
} ```
[1]: https://i.stack.imgur.com/O7Pnc.png
[2]: https://i.stack.imgur.com/0g5HF.png

How to constraints width and height of `Scaffold` (useful for Web and Desktop)

The screenshot below is a Scaffold on Desktop (should be similar on browser):
Is it possible to constraints Scaffold to a min size (width and height)?
If screen width is smaller than the min width, a horizontal scrollbar appears to move left and right.
If screen height is smaller than the min height, a vertical scrollbar appears to move up and down.
Ex. designers in our project want a min width of 960px on browser.
Here's an example main.dart that uses LayoutBuilder to constrain the scaffold (both the body and the appBar) to a min of 480dp, and if the width constraints are less than that, wraps the scaffold inside a horizontal ScrollView with a ScrollBar. And if the constraint height is less than 480dp, it wraps scaffold (which may already be wrapped or not) in a vertical scroll.
If both width and height are less than 480dp, 2 scrollbars are visible. In this case, the widget tree must be ScrollBar > ScrollBar > ScrollView > ScrollView. If widget tree is ScrollBar > ScrollView > ScrollBar > ScrollView, the nested Scrollbar is only visible when the parent ScrollBar is scrolled to the edge side.
import 'package:flutter/material.dart';
main() {
runApp(MaterialApp(
// set default isAlwaysShown, so don't need to set for individual Scrollbar.
theme: ThemeData(scrollbarTheme: ScrollbarThemeData(isAlwaysShown: true)),
home: App(),
));
}
class App extends StatefulWidget {
#override
AppState createState() => AppState();
}
class AppState extends State<App> {
final minWidth = 480.0;
final minHeight = 480.0;
ScrollController _horizontalController = ScrollController();
ScrollController _verticalController = ScrollController();
#override
void dispose() {
_horizontalController.dispose();
_verticalController.dispose();
super.dispose();
}
Widget _buildScaffold() {
return Scaffold(
appBar: AppBar(title: Text('2D Scrollbars')),
body: Container(color: Colors.amber),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.school), label: 'School'),
],
),
);
}
#override
Widget build(BuildContext context) {
final scaffold = _buildScaffold();
return LayoutBuilder(
builder: (context, constraints) {
final horizontalScrollbarEnabled = constraints.minWidth < minWidth;
final verticalScrollbarEnabled = constraints.minHeight < minHeight;
if (horizontalScrollbarEnabled && verticalScrollbarEnabled) {
return Scrollbar(
controller: _horizontalController,
child: Scrollbar(
// IMPORTANT: this scrollbar only handle notification of the vertical ScrollView.
// The first ScrollView (depth = 0), is the horizontal one.
// The second ScrollView (depth = 1), is the vertical one.
// If notification.depth != 1, the notification is bubble up to horizontal Scrollbar.
notificationPredicate: (notification) => notification.depth == 1,
controller: _verticalController,
child: SingleChildScrollView(
controller: _horizontalController,
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
controller: _verticalController,
child: Container(
width: minWidth,
height: minHeight,
child: scaffold,
),
),
),
),
);
} else if (horizontalScrollbarEnabled) {
return Scrollbar(
controller: _horizontalController,
child: SingleChildScrollView(
controller: _horizontalController,
scrollDirection: Axis.horizontal,
child: Container(
width: minWidth,
child: scaffold,
),
),
);
} else if (verticalScrollbarEnabled) {
return Scrollbar(
controller: _verticalController,
child: SingleChildScrollView(
controller: _verticalController,
child: Container(
height: minHeight,
child: scaffold,
),
),
);
}
return scaffold;
},
);
}
}