How to include two items as a page main content in pageview -- Flutter - flutter

Is it possible to use PageView to reach the following work?
I'd like to use two containers as the main content for a page in PageView.
I've tried to adjust viewPortFraction but it doesn't work as I expected.
The following effect is used by Apple App store and Prime Video and many apps.
Thanks.

You could try to use an horizontal ListView with PageScrollPhysics() In the physics param.

Instead of using PageView, you could just use a ListView with
scrollDirection: Axis.horizontal
This way you could achieve the same result. You would need to somehow provide an explicit height to your ListView if I am not mistaken.
Below is a working sample.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Example'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Top Games",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
),
),
Container(
height: 200,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
GameCard(
"The Game 1",
"A puzzle game",
"https://lorempixel.com/200/200/abstract/",
),
GameCard(
"The Game 2",
"An even beter game",
"https://lorempixel.com/200/200/sports/2/",
),
GameCard(
"SportMania",
"Editors Choice",
"https://lorempixel.com/200/200/sports/3/",
),
GameCard(
"MonkeySports",
"Monkeys playing Sport",
"https://lorempixel.com/200/200/abstract",
),
],
),
)
],
),
);
}
}
class GameCard extends StatelessWidget {
final String gameTitle;
final String gameDescr;
final String imgUrl;
GameCard(
this.gameTitle,
this.gameDescr,
this.imgUrl,
);
#override
Widget build(BuildContext context) {
return Card(
elevation: 3,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
margin: EdgeInsets.all(5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
imgUrl,
width: 180,
height: 130,
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
Container(
padding: EdgeInsets.all(4),
width: 180,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
gameTitle,
maxLines: 2,
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(
gameDescr,
maxLines: 2,
),
],
),
),
],
),
);
}
}
Hope this gets you started.

You can using viewportFraction params in PageController;

Related

I can't display 4 Card in middle of page with Flutter

import 'package:flutter/material.dart';
class CardNote extends StatelessWidget {
const CardNote({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Card(
elevation: 3,
color: Colors.amber,
child: SizedBox(
width: 200,
height: 200,
child: Center(
child: Text(
'Card 1',
)),
),
),
Card(
elevation: 3,
color: Colors.amber,
child: SizedBox(
width: 200,
height: 200,
child: Center(
child: Text(
'Card 2',
)),
),
),
],
),
),
),
);
}
}
When I add this widget to the page where I want to show the cards, I can show 2 widgets in a single line. Then when I call the same widget again, I get the following error.
RenderCustomMultiChildLayoutBox object was given an infinite size during layout.This probably means that it is a render object that tries to be as big as possible, but it was put inside another render object that allows its children to pick their own size.
return Scaffold(
body: Container(
child: Row(
children: const [
CardNote(),
SizedBox(
height: 50,
),
CardNote()
],
),
),
);
This is how I add the cards to the page I want to show.
This is how I want to show 4 cards in the middle of the page
This is how it looks on my page. It shows at the top of the page, not in the middle.
You can wrap your CardNote widget with Expanded widget. And use a top level scrollable widget. Also you dont need to us multiple scaffold
home: Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: const [
CardNote(),
SizedBox(
height: 50,
),
CardNote(),
],
),
),
),
While the card is fixed width, you can provide it
class CardNote extends StatelessWidget {
const CardNote({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Card(
elevation: 3,
color: Colors.amber,
child: SizedBox(
width: 200,
height: 200,
child: Center(
child: Text(
'Card 1',
)),
),
),
Card(
elevation: 3,
color: Colors.amber,
child: SizedBox(
width: 200,
height: 200,
child: Center(
child: Text(
'Card 2',
)),
),
),
],
),
),
);
}
}
You are trying to add a row which has alignment of spaceEven inside another row. When you added the first set of cards it got aligned evenly. On second set you have 2 set of rows inside a row each one has alignment of spaceEven..
Depending on the layout if you wish to show these cards one below the other set then use a Column widget
return Scaffold(
body: Container(
child: Column(//here
children: const [
CardNote(),
SizedBox(
height: 50,
),
CardNote()
],
),
),
);

Flutter ListView not scrolling (I feel like I've tried every solution on the internet)

If I drag and hold my finger down I can see a few items that are below the cutoff of the screen but as soon as I let go, it just bounces back to the top. I tried using SingleChildScrollView places, tried setting primary = true, and a bunch of other stuff that didn't help. I'm fairly new to flutter so any help would be appreciated!! Let me know if any more info is needed.
Here is my code:
import 'package:flutter/material.dart';
import 'package:drink_specials/models/restaurant.dart';
import 'package:drink_specials/screens/home/restaurant_list.dart';
class RestaurantNameTextStyle {
static TextStyle display5(BuildContext context) {
return Theme.of(context).textTheme.headline2.copyWith(color: Colors.white);
}
}
class RestaurantTypeTextStyle {
static TextStyle display5(BuildContext context) {
return Theme.of(context).textTheme.headline6.copyWith(color: Colors.white);
}
}
class RestaurantDetail extends StatelessWidget {
final Restaurant restaurant;
RestaurantDetail({Key key, #required this.restaurant}) : super(key: key);
#override
Widget build(BuildContext context) {
final topContentText = Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: 100.0),
Text(
restaurant.name,
style: RestaurantNameTextStyle.display5(context),
),
SizedBox(height: 10.0),
Expanded(
flex: 6,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(
restaurant.restaurant_type,
style: RestaurantTypeTextStyle.display5(context),
))),
],
);
final topContent = Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 10.0),
height: MediaQuery.of(context).size.height * 0.5,
decoration: new BoxDecoration(
image: new DecorationImage(
image: NetworkImage(restaurant.photo),
fit: BoxFit.cover,
),
)),
Container(
height: MediaQuery.of(context).size.height * 0.5,
padding: EdgeInsets.all(40.0),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, .9)),
child: Center(
child: topContentText,
),
),
Positioned(
left: 8.0,
top: 60.0,
child: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, color: Colors.white),
),
)
],
);
final bottomContent = Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(8.0),
child: Center(
child: ListView.builder(
scrollDirection: Axis.vertical,
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
itemCount: restaurant.specials.length,
itemBuilder: (context, index) {
final item = restaurant.specials[index];
return Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, 1.0)),
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal:20, vertical:10),
title: Text(item, style: TextStyle(color: Colors.white)),
)
),
);
}
),
),
);
return Scaffold(
body: Column(
children: <Widget>[
topContent,
Expanded(
child: bottomContent,
),
],
),
);
}
}
There is a ListView inside a SingleChildScrollView and both of them are scrollable. Scrolling on one of them should be disabled.
As they already explained. If you have a ListView.builder, you don't need SingleChildScrollView.
Try removing SingleChildScrollView. The code should look like this:
Scaffold(
body: Column(
children: <Widget>[
topContent,
Expanded(
child: bottomContent,
),
],
),
);
ListView already have scroll behavior so you won't need some SingleChildScrollView

What widget should I use for center text and left-aside button

I am making layout for flutter application
What I want to do is
-----------------------
|[i] [text] |
| |
Icon should be the left (padding 5px)
And text should be the center of screen.
At first I should use the Column
However my layout is not the same proportion
It might be simple though , how can I make it??
Stack() is one of the many options that you can use. Something like this:
Stack(
children:<Widget>[
Padding(
padding: EdgeInsets.only(left: 5),
child: Icon(Icons.info),
),
Align(
alignment: Alignment.topCenter,
child: Text("I'm on the top and centered."),
),
],
),
One way you can do this is something like this..
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: EdgeInsets.all(5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.info),
Text('text'),
Opacity(opacity: 0, child: Icon(Icons.info)),
],
),
),
Padding(
padding: EdgeInsets.all(5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.info),
Text('second text'),
Opacity(opacity: 0, child: Icon(Icons.info)),
],
),
),
],
);
}
Result:
I might be late, but I want this answer to be there, if some one would find it better for the development purposes in future time.
We can make a reusable widget, which we can use it inside the main widget. The widget will accept text, and icon to be passed when called
// IconData is the data type for the icons
Widget myWidget(String text, IconData icon) => Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
// this will be used a left-padding param
SizedBox(width: 20.0),
// using it here
Icon(icon, size: 28.0, color: Colors.greenAccent),
SizedBox(width: 5.0),
// this will take the remaining space, and then center the child
Expanded(child: Center(child: Text(text)))
]
);
To use in the main Widget, just do like this:
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// calling our widget here
myWidget('FirstText', Icons.alarm),
SizedBox(height: 20.0), // this is used as a top margin
myWidget('SecondText', Icons.notifications)
]
)
If you wanna check out the sources which I used, please see:
Expanded class
SizedBox class
The result you will get is this:
This was an answer I gave a while ago for this same issue, but the other situation was vertical (a Column) instead of horizontal (a Row). Just swap the Row and Columns out for a Column and Rows in this example and you'll get the idea.
My code has grey added in order to show the difference between the two approaches.
A Stack will work, but it's overkill for this, this kind of problem is part of why we have Expandeds and Flexibles. The trick is to use three Flexibles (2 Expandeds and a Spacer). Put the Spacer on top. It and the bottom Expanded must have the same flex value in order to center the middle Expanded.
import 'package:flutter/material.dart';
class CenteringOneItemWithAnotherItemInTheColumn extends StatelessWidget {
const CenteringOneItemWithAnotherItemInTheColumn({
Key key,
}) : super(
key: key,
);
/// Adjust these values as needed
final int sameFlexValueTopAndBottom = 40; // 40%
final int middleFlexValue = 20; // 20%
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Column Alignment Question'),
),
body: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Spacer(),
const Text(
'Cent',
style: TextStyle(
fontSize: 64,
),
),
const Spacer(),
const Text(
'Bot',
style: TextStyle(
fontSize: 64,
),
),
],
),
Column(
children: <Widget>[
/// The key is to have the Spacer and the bottom Expanded
/// use the same value for flex. That will cause the middle
/// child of the Column to be centered vertically.
Expanded(
flex: sameFlexValueTopAndBottom,
child: Container(
width: 100,
color: Colors.grey[300],
),
),
Expanded(
flex: middleFlexValue,
child: const Align(
alignment: Alignment.center,
child: Text(
'Cent',
style: TextStyle(
fontSize: 64,
),
),
),
),
Expanded(
flex: sameFlexValueTopAndBottom,
child: Container(
color: Colors.grey[300],
child: const Align(
alignment: Alignment.bottomCenter,
child: Text(
'Bot',
style: TextStyle(
fontSize: 64,
),
),
),
),
),
],
),
],
),
);
}
}

textAlign: Textalign.center not coming in center?why

class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: CircleAvatar(
radius: 40.0,
child: Image.asset('images/pic.jpg'),
),
),
Text(
"michael dub",
),
Text("FLUTTER DEVELOPER"),
Container(
width: 350.0,
height: 70.0,
child: Card(
child: Row(
children: <Widget>[
Icon(
Icons.phone,
),
Text(
"+12-345-678-910",
textAlign: TextAlign.center,
),
],
)))
],
),
),
);
}
}
in your column, try crossAxisAlignment: CrossAxisAlignment.stretch
EDIT:
if you're trying to do what I think you're trying to do, try adding
mainAxisAlignment: MainAxisAlignment.center
to your row
you need to wrap your text widget with Expanded widget. Default Row Widget will not take full width and it is creating issue.
Check out following code.
Expanded(
child: Text(
"+12-345-678-910",
textAlign: TextAlign.center,
),
),

How to limit draggable scrollable sheet to take height according to its child height in flutter?

I am using draggableScrollableSheet. I am giving these parameters
DraggableScrollableSheet(initialChildSize: 0.4,maxChildSize: 1,minChildSize: 0.4,builder: (BuildContext context, ScrollController scrollController) {
return SingleChildScrollView(controller: scrollController,
child: Theme(
data: Theme.of(context).copyWith(canvasColor: Colors.transparent),
child: Opacity(
opacity: 1,
child: IntrinsicHeight(
child: Column(mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(height: 10,),
Container(
margin: EdgeInsets.only(right: 300),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: Colors.blue,
width: 3,
style: BorderStyle.solid),
),
),
),
Card(
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
S
.of(context)
.we_have_found_you_a_driver,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 10,
),
Text(S
.of(context)
.driver_is_heading_towards +
' ${widget.order.foodOrders.first.food.restaurant.name}')
],
),
),
],
),
elevation: 5,
),
SizedBox(height: 10,),
Card(
elevation: 5,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CircleAvatar(
radius: 50.0,
backgroundColor: Colors.white,
child:
Image.asset(
'assets/img/image_not_available.jpg'),
),
Expanded(
child: Column(mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Expanded(
child: Text('Test',
textAlign: TextAlign.start,
style: new TextStyle(
color: Colors.black,
fontSize: 16.0,
)),
),
Icon(Icons.star, color: Colors.yellow.shade700,)
],
),
SizedBox(height: 30,),
Row(mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Expanded(
child: Container(
child: Text('Mobile number',
textAlign: TextAlign.start,
style: new TextStyle(
color: Colors.black,
fontSize: 16.0,
)),
),
),
Icon(Icons.phone,),
SizedBox(width: 10,),
Icon(Icons.message),
],
),
],
),
)
]),
),
SizedBox(height: 10,),
Card(
child: Align( alignment: Alignment(-1,1),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
S
.of(context)
.you_ordered_from + ' ${widget.order.foodOrders.first.food.restaurant.name}',
style: TextStyle(
color: Colors.grey,
),
),
SizedBox(
height: 5,
),
Column(children: List.generate(widget.order.foodOrders.length,(index) {
return Text(
'${widget.order.foodOrders[index].food.name}'
);
},),),
Row(
children: <Widget>[
Column(crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('See details', style: TextStyle(fontWeight: FontWeight.bold,color: Colors.blue),),
],
),
],
),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
SizedBox(height: 40,),
Row(
children: <Widget>[
Icon(Icons.monetization_on),
Text(widget.order.foodOrders
.first.price
.toString()),
],
),
],
),
),
],
),
),
elevation: 5,
),
],
),
),
),
),
)
and I also used a single child scroll view and column so that I can show my cards in that column of draggableScrollableSheet. But I want draggableScrollableSheet to take height dynamically instead of defining size. Like now I want to show only 2 to 3 cards and that is taking full screen. But I want it to take the minimum height of the screen. How can we achieve this?
I was struggling with this for a while, and then discovered that the correct way to achieve this is to use ClampingScrollPhysics as the physics parameter of the scroll view.
https://api.flutter.dev/flutter/widgets/ClampingScrollPhysics-class.html
I'm a week into Flutter but I found a solution to this. It might be substandard so correct me if I'm wrong.
So what I've done is create a variable called bsRatio for the bottom sheet. This is will be the height of the child view/widget (or bottom sheet content) divide by the height of the parent/screen. This ratio should be set to the maxChildSize and probably even the initialChildSize of your DraggableScrollableSheet.
So in your parent widget or Widget State class add something like this.
class ParentWidget extends StatefulWidget {
ParentWidget({Key? key}) : super(key: key);
#override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
var bsRatio = 0.4; // Set an initial ratio
#override
Widget build(BuildContext context) {
// The line below is used to get status bar height. Might not be required if you are not using the SafeArea
final statusBarHeight = MediaQuery.of(context).viewPadding.top;
// If you are not using SafeArea Widget you can skip subtracting status bar height from the Window height
final windowHeight = MediaQuery.of(context).size.height - statusBarHeight;
// This below is a callback function that will be passed to the child Widget of the DraggableScrollableSheet ->
childHeightSetter(childHeight) {
// setState rebuilds the UI with the new `bsRatio` value
setState(() {
// The new bottom sheet max height ratio is the height of the Child View/Widget divide by the screen height
bsRatio = childHeight / windowHeight;
});
}
return Scaffold(
backgroundColor: Colors.black12,
body: SafeArea(
child: Stack(
children: [
const SomeBackgroundView(),
DraggableScrollableSheet(
initialChildSize: bsRatio, // here you set the newly calculated ratio as the initial height of the Bottom Sheet
minChildSize: 0.2,
maxChildSize: bsRatio, // here you set the newly calculated ratio as the initial height of the Bottom Sheet
snap: true,
builder: (_, controller) {
return LayoutBuilder(builder: (_, box) {
// Added a container here to add some curved borders and decent looking shadows via the decoration property
return Container(
child: SingleChildScrollView(
controller: controller,
// The child view/widget `MyBottomSheet` below is the actual bottom sheet view/widget
child: MyBottomSheet(childHeightSetter: childHeightSetter),
),
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 5.0,
spreadRadius: 2.0
)
],
borderRadius: BorderRadius.all(Radius.circular(20.0))
),
);
});
},
),
],
),
),
);
}
}
And this would be your child view/widget (also your BottomSheet view/widget)
class MyBottomSheet extends StatefulWidget {
// This below is the local callback variable. The `?` is because it may not be set if not required
final ValueSetter<double>? childHeightSetter;
const MyBottomSheet({Key? key, this.childHeightSetter}) : super(key: key);
#override
_MyBottomSheetState createState() => _MyBottomSheetState();
}
class _LoginBottomSheetState extends State<LoginBottomSheet> {
// bsKey is the key used to reference the Child widget we are trying to calculate the height of. Check the `Card` container below
GlobalKey bsKey = GlobalKey();
// this method will me used to get the height of the child content and passed to the callback function so it can be triggered and the ratio can be calculated and set in the parent widget
_getSizes() {
final RenderBox? renderBoxRed =
bsKey.currentContext?.findRenderObject() as RenderBox?;
final cardHeight = renderBoxRed?.size.height;
if (cardHeight != null)
super.widget.childHeightSetter?.call(cardHeight);
}
// This is the function to be called after the Child has been drawn
_afterLayout(_) {
_getSizes();
}
#override
void initState() {
super.initState();
// On initialising state pass the _afterLayout method as a callback to trigger after the child Widget is drawn
WidgetsBinding.instance?.addPostFrameCallback(_afterLayout);
}
#override
Widget build(BuildContext context) {
return Card(
key: bsKey, // This is the key mentioned above used to calculate it's height
color: Colors.white,
shadowColor: Colors.black,
elevation: 40.0,
margin: EdgeInsets.zero,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0))),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
// Random children for bottom sheet content
const SizedBox(height: 10.0),
Center(
child: Container(
child: const SizedBox(width: 40.0, height: 5.0),
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(5.0)
),
),
),
const SizedBox(height: 10.0),
const AnotherBottomSheetContentView()
],
),
);
}
}
the initialChildSize is the height of your ScrollView before its actually scrolled, so that means you can actually decide what it would look like.
here is an example![the draggable scrollsheet here has initialChildSize: 0.1,maxChildSize: 1,minChildSize: 0.1,
]1