Let's say that I want create a Flutter app. In my app I want to create a Row widget with the following children:
AspectRatio(aspectRatio: 1, child: Center(child: Text("I am text!"))
Image.asset("path/to/asset.png") with unknown aspect ratio
I want to display that Row while keeping both aspect ratios intact. How can I do that?
Here is my attempt:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("This is my app")),
body: Container(
child: Row(children: [buildItem1(), buildItem2()]),
),
);
}
Widget buildItem1() {
return AspectRatio(
aspectRatio: 1,
child: Container(
color: Colors.red, child: Center(child: Text("This is my text"))));
}
Widget buildItem2() {
return Image.asset("path/to/my/asset.jpg");
}
}
And this is what being displayed:
I am trying to figure out a way to automatically shrink the row height such that both items would fit the screen.
If I use a fixed hight like that:
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("This is my app")),
body: Container(
height: 300,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [buildItem1(), buildItem2()],
),
),
);
}
...
Than I get the following:
Which is better, but I want to fill the entire width of the screen.
Thanks for anyone who can help!
You can use Expanded on second widget, that will get the avialable space on row. Also for the image use fit: BoxFit.cover(better looks than width).
body: Container(
height: 300,
child: Row(
children: [buildItem1(), Expanded(child: buildItem2())],
),
),
Widget buildItem2() {
return Image.asset(
"assets/images/user.png",
fit: BoxFit.cover,
);
}
Now if you want full screen, I will prefer
Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Container(
color: Colors.red,
child: Center(
child: Text("This is my text"),
)),
),
Expanded(
child: Image.asset(
"assets/images/user.png",
fit: BoxFit.cover,
)),
],
),
Also LayoutBuilder on scaffold's body provide more options. Also check IntrinsicHeight.
Related
There are two main widgets:
I have this OverflowingWidget that is basically a Column - in reality this contains many widgets - However for this example, it has been given a height bigger than its parent widget.
The Parent widget ContainerWidget is basically AnimatedSwitcher which is basically a Stack contained by Column which constrained by a hight less than OverflowingWidget.
As you can see in dart-pad there is an overflow. The OverflowingWidget.
import 'package:flutter/material.dart';
const 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: ContainerWidget(),
),
),
);
}
}
class ContainerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
height: 100,
width: 50,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: ClipRect(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: OverflowingWidget(),
layoutBuilder: (currentChild, previousChildren) => Stack(
children: [
...previousChildren,
if (currentChild != null) currentChild,
],
)),
),
),
],
));
}
}
class OverflowingWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: [Container(color: Colors.blue, height: 150, width: 50)]);
}
}
Desired result:
I need the OverflowingWidget to be clean clipped without trying to resize the content inside. The same effect that can be obtained when in css:overflow:hidden.
AnimatedSwitcher is getting height 100, but it needs 150 for current widget. you can increase the height on redContainer.
Instead of using Column, you can use Stack widget.
class ContainerWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
height: 150,
width: 50,
child: Stack(
children: [
ClipRect(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: OverflowingWidget(),
layoutBuilder: (currentChild, previousChildren) => Stack(
children: [
...previousChildren,
if (currentChild != null) currentChild,
],
)),
),
],
));
}
}
I found a way to clip an overflowing column/row. Simply wrap that column/row with Wrap widget. That will do the clipping.
class OverflowingWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Wrap(children: [
Column(children: [
Container(
color: Colors.blue,
height: 150,
width: 50)
])
]);
}
}
I am trying to make an audio player app,
and I want to make the player screen fit the whole screen size.
However, the padding at the top and at the bottom doesn't help.
I tried to remove the SafeArea from bottomNavigationBar and other widgets and it didn't work.
How can I handle this?
Image of the player:
(the gray color padding doesn't let the image stretch to the end)
the code of the player:
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xff1c1c1e),
body: GetBuilder<OverlayHandler>(
builder: (getContext) {
if (!Get.find<OverlayHandler>().inPipMode) {
return Stack(
children:[
Container(...)
]
); // player at full screen
} else {
return Stack(...); // player at PiP mode
}
}
)
);
}
the code of the main screen widget:
Widget build(BuildContext context) {
return GetBuilder<NavigationController>(
builder: (controller) {
return Scaffold(
body: SafeArea(
// bottom option of this SafeArea doesn't affect the player size
child: IndexedStack(
index: controller.tabIndex,
children: const [
...
],
),
),
bottomNavigationBar: SafeArea(
// bottom option of this SafeArea doesn't affect the player size
child: SizedBox(
height: 80,
child: BottomNavigationBar(
items: [
...
],
),
),
),
);
}
);
}
}
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
#override
State<HomeScreen> createState() => _HomeScreenState();
}
TextEditingController controller = TextEditingController();
bool hasHash = false;
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Container(
height: double.infinity,
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
"https://cdn.pixabay.com/photo/2016/09/10/11/11/musician-1658887_1280.jpg",
),
),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
height: 300,
width: double.infinity,
color: Colors.black.withOpacity(.7),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Icon(
Icons.skip_previous_rounded,
size: 55,
color: Colors.white,
),
Icon(
Icons.play_circle_fill_rounded,
size: 110,
color: Colors.white,
),
Icon(
Icons.skip_next_rounded,
size: 55,
color: Colors.white,
),
],
),
),
),
],
),
);
}
}
Android screenshot
iOS screenshot
Try removing the Scaffold()'s background color and add extendBody: true, or set the height of the container to height: double.infinity, or inside the stack just add and empty container with height as height: double.infinity,
I am trying to achieve an effect where there is expandable content on the top end of a sidebar, and other links on the bottom of the sidebar. When the content on the top expands to the point it needs to scroll, the bottom links should scroll in the same view.
Here is an example of what I am trying to do, except that it does not scroll. If I wrap a scrollable view around the column, that won't work with the spacer or expanded that is needed to keep the bottom links on bottom:
import 'package:flutter/material.dart';
const 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 StatefulWidget {
#override
State<MyWidget> createState() {
return MyWidgetState();
}
}
class MyWidgetState extends State<MyWidget> {
List<int> items = [1];
#override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
setState(() {
items.add(items.last + 1);
});
},
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
setState(() {
if (items.length != 1) items.removeLast();
});
},
),
],
),
for (final item in items)
MyAnimatedWidget(
child: SizedBox(
height: 200,
child: Center(
child: Text('Top content item $item'),
),
),
),
Spacer(),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all()),
height: 200,
child: Text('Bottom content'),
)
],
);
}
}
class MyAnimatedWidget extends StatefulWidget {
final Widget? child;
const MyAnimatedWidget({this.child, Key? key}) : super(key: key);
#override
State<MyAnimatedWidget> createState() {
return MyAnimatedWidgetState();
}
}
class MyAnimatedWidgetState extends State<MyAnimatedWidget>
with SingleTickerProviderStateMixin {
late AnimationController controller;
#override
initState() {
controller = AnimationController(
value: 0, duration: const Duration(seconds: 1), vsync: this);
controller.animateTo(1, curve: Curves.linear);
super.initState();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return SizedBox(height: 200 * controller.value, child: widget.child);
});
}
}
I have tried using a global key to get the size of the spacer and detect after rebuilds whether the spacer has been sized to 0, and if so, re-build the entire widget as a list view (without the spacer) instead of a column. You also need to listen in that case for if the size shrinks and it needs to become a column again, it seemed to make the performance noticeably worse, it was tricky to save the state when switching between column/listview, and it seemed not the best way to solve the problem.
Any ideas?
Try implementing this solution I've just created without the animation you have. Is a scrollable area at the top and a persistent footer.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
home: SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("My AppBar"),
),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
// Your scrollable widgets here
Container(
height: 100,
color: Colors.green,
),
Container(
height: 100,
color: Colors.blue,
),
Container(
height: 100,
color: Colors.red,
),
],
),
),
),
Container(
child: Text(
'Your footer',
),
color: Colors.blueGrey,
height: 200,
width: double.infinity,
)
],
),
),
),
);
}
}
I'm new in flutter world. I'm trying to put a little bit this logo up and add text above it.
I found that I need Stack and Positioned Widget to position my logo and sure I can position this logo with i.e: top: 20, bottom: 20 etc but then I thought, I'm positioning it versus my emulator with certain height and weidth, what about devices with other resolutions? It should be dynamic I guess?
Could you tell me how can I achieve it?
Here is how it should looks:
How it looks now (dont mind about this logo, it's just an example)
And my another question. How can I add some text above this logo?
The code I wrote:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
backgroundColor: const Color(0xFEC2C2C2),
body: Stack(
children: <Widget>[
Positioned.fill(
child: Image(
image: AssetImage('assets/images/logo.png'),
),
),
],
),
),
),
);
}
try this code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(50.0),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('This is My Brand Name',
style: Theme.of(context).textTheme.headline5),
SizedBox(
height: 20.0,
),
FlutterLogo(
size: 150,
)
],
),
),
);
}
}
Demo: https://i.stack.imgur.com/FTPqh.jpg
I am building an app where I wanted to show 4 containers covering the whole available space of on the phone.
For that I am getting the full width and height of the screen using MediaQuery.of. I make the 4 containers fill the entire screen by giving each of them a height of 0.25 of the total height.
The initial code comes here and works fine:
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: Scaffold(body: MyHomePage(title: 'Title')),
);
}
}
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 _buildMobileLayout(context);
}
Widget _buildMobileLayout(BuildContext context) {
AppBar appBar = AppBar(
title: Text("My App Title"),
);
// var newHeight =
// (MediaQuery.of(context).size.height - appBar.preferredSize.height) *
// 0.25;
var newHeight = (MediaQuery.of(context).size.height) * 0.25;
return Scaffold(
//appBar: appBar,
body: Column(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: newHeight,
child: Center(child: Text("Cont 1")),
),
Container(
width: MediaQuery.of(context).size.width,
height: newHeight,
child: Center(child: Text("Cont 2")),
),
Container(
width: MediaQuery.of(context).size.width,
height: newHeight,
child: Center(child: Text("Cont 3")),
),
Container(
width: MediaQuery.of(context).size.width,
height: newHeight,
child: Center(child: Text("Cont 4")),
),
],
),
);
}
}
As you can see on the code above, I am not including the appBar yet, since here is when the problem comes. All works ok without the appBar, but when I include the appBar, even though I am taking care of its height, getting a new total height and splitting it among the 4 containers, I get an error by pixel overflow on the last container.
Phone: Huawei P20 Pro.
Instead of using MediaQuery you can wrap each of your widgets in a Expanded and give them the same flex.
return Scaffold(
//appBar: appBar,
body: Column(
children: <Widget>[
Expanded(
flex: 1,
child: Center(child: Text("Cont 1")),
),
Expanded(
flex: 1,
child: Center(child: Text("Cont 2")),
),
Expanded(
flex: 1,
child: Center(child: Text("Cont 3")),
),
Expanded(
flex: 1,
child: Center(child: Text("Cont 4")),
),
],
),
);