Flutter: How to reduce size of CupertinoPicker in ModalBottomSheet? - flutter

Whenever I try to set up a CupertinoPicker in a ModalBottomSheet in a Column, Row, or in a Container it seems to occupy the maximum possible place.
Is there anyway to limit the size/heigt of the ModalBottomSheet to the actual size of the CupertinoPicker, which is obviously much smaller in height than the ModalBottomSheet (as becomes apparent through its grey background color).
Here is my code:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.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:
Center(child: RaisedButton(onPressed: () => showPicker(context),
child: Text('Show Bottom Sheet')),)
);
}
Widget showPicker(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('This is a picker'),
Expanded(
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: Container(
height: 100,
child: CupertinoPicker(
itemExtent: 30,
onSelectedItemChanged: (int index) {
print(index);
},
children: <Widget>[
Center(child: Text("Item 1")),
Center(child: Text("Item 2")),
Center(child: Text("Item 3")),
],
),
),
),
],
),
),
],
),
);
});
}
}
This is what I get:
Thank you for any tips, hints and advice!

Widget showPicker(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('This is a picker'),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: Container(
height: 100,
child: CupertinoPicker(
itemExtent: 30,
onSelectedItemChanged: (int index) {
print(index);
},
children: <Widget>[
Center(child: Text("Item 1")),
Center(child: Text("Item 2")),
Center(child: Text("Item 3")),
],
),
),
),
],
),
],
),
);
});
}
This can solve the issue paste the method, you will get your desired output

Related

ChoiceChip not expanding with IntrinsicWidth

I'm wondering why ChoiceChip is not expanding like ElevatedButton does.
Here is an example: (look at dartpad)
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyWidget(),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(children: [
IntrinsicWidth(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: ElevatedButton(
child: const Text('Larger Text'),
onPressed: () {},
)),
Expanded(
child: ElevatedButton(
child: const Text('Text'),
onPressed: () {},
)),
],
)),
IntrinsicWidth(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: const [
Expanded(
child: ChoiceChip(
label: Text('Larger Text'),
selected: false,
)),
Expanded(
child: ChoiceChip(
label: Text('Text'),
selected: false,
)),
],
))
]);
}
}
To solve this, let's start by enabling Flutter Inspector, we can see the second IntrinsicWidth width and its children don't fill their parent as the first one (two problems)
Solve 1st problem: IntrinsicWidth children don't fill their parent width
So, the problem is the size of the 2nd IntrinsicWidth's children is not wide/big enough so that it can be full of parent width. Try increasing its width manually by wrapping it in a Container with double.infinity width, like this:
ChoiceChip(
label: Container(width: double.infinity, child: Text('Larger Text')),
selected: false,
)
New result:
Solve 2nd problem: the 2nd IntrinsicWidth don't fill their parent width
Let's leave Column children can have the maximum width as it is (removing all IntrinsicWidth inside its children) and then wrap the Column by an IntrinsicWidth. Complete sample code:
Sample code
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: MyWidget(),
),
);
}
}
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
#override
Widget build(BuildContext context) {
return IntrinsicWidth(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: ElevatedButton(
child: const Text('Larger Text'),
onPressed: () {},
),
),
Expanded(
child: ElevatedButton(
child: const Text('Text'),
onPressed: () {},
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: ChoiceChip(
label: Container(width: double.infinity, child: Text('Larger Text')),
selected: false,
),
),
Expanded(
child: ChoiceChip(
label: Container(width: double.infinity, child: Text('Text')),
selected: false,
),
),
],
),
],
),
);
}
}
Final result:
Because under the hood the widgets are designed differently (that's why the names).
I haven't checked the code in detail (you can do that by ctrl + click on Widget Name).
But best guess, visual density is lower in chips for being compact and higher in buttons.
More on technical difference to understand which to use : chips-vs-button

Flutter showModalBottomSheet to show ontop of BottomNavigationBar

I want to show a modalsheet like this
above the BottomsNavigationBar like so. I have tried this: But then my whole bottomNavigationBar menu becomes unclickable.
My code for this is:
Widget build(BuildContext context) {
final theme = Theme.of(context);
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
return WillPopScope(
onWillPop: () => _willPopCallback(context),
child: Scaffold(
key: _scaffoldKey,...
bottomNavigationBar: BottomNavigationBar(
onTap: (v) {
_scaffoldKey.currentState!.showBottomSheet<Null>(
(BuildContext context){
return GridView.count....
}
}).....
Then this is my original code:
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return WillPopScope(
onWillPop: () => _willPopCallback(context),
child: Scaffold(
body: PageView(
controller: _controller,
physics:const NeverScrollableScrollPhysics(),
onPageChanged: (v) => setState(() => _selectedIndex = v),
children: BottomNavigationList.pageList(context),
),
bottomNavigationBar: BottomNavigationBar(
onTap: (v) {
setState(() {
if (v == 3) {
showModalBottomSheet(
...
builder: (BuildContext context){
return GridView.count...
but then it is going ontop of the BottomNavigationBar. Like this:
Is there any way I can have it clipped on top of the BottomNavigationBar like in the first image or a FAB
UPDATE: I have tried the suggested implementation and got this:
Maybe let me try to rephrase: so the first image has 3 rows, the most bottom row is the bottomNavigationBar. When and if you click on it when you are on that selectedIndex of the bottomNav, the other two rows have to show, WITHOUT obscuring the bottomNav. #Yeasin, in your solution there, the purple row has to show when the hamburger menu is pressed, and hide when pressed again that is why I had used the showModalBottomSheet and also tried the showBottomSheet
You can use Stack Or Column with boolean to handle view.
Using Column
class _CustomViewState extends State<CustomView> {
bool _showBottomSheet = false;
#override
Widget build(BuildContext context) {
return Scaffold(body: LayoutBuilder(
builder: (context, constraints) {
return Column(
children: [
Expanded(
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: ElevatedButton(
onPressed: () {
setState(() {
_showBottomSheet = !_showBottomSheet;
});
},
child: Text(
"show btmSheet",
)),
),
],
),
),
if (_showBottomSheet)
SizedBox(
//get single gridWith * mainAxisCount
height: constraints.maxWidth / 4 * 2, //based on your view
child: GridView.count(
crossAxisCount: 4,
physics: NeverScrollableScrollPhysics(),
children: [
...List.generate(
8,
(index) => Container(
color: Colors.pink,
child: Text("$index"),
),
)
],
),
),
Container(
width: constraints.maxWidth,
color: Colors.deepPurple,
height: kToolbarHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
...List.generate(
4,
(index) => ElevatedButton(
onPressed: () {
print("tapped on $index");
},
child: Text("$index"),
),
)
],
),
)
],
);
},
));
}
}
Using Stack
class CustomView extends StatefulWidget {
CustomView({Key? key}) : super(key: key);
#override
_CustomViewState createState() => _CustomViewState();
}
class _CustomViewState extends State<CustomView> {
bool _showBottomSheet = false;
#override
Widget build(BuildContext context) {
return Scaffold(body: LayoutBuilder(
builder: (context, constraints) {
return Stack(
children: [
Align(
alignment: Alignment.center, // based on UI,
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: ElevatedButton(
onPressed: () {
setState(() {
_showBottomSheet = !_showBottomSheet;
});
},
child: Text(
"show btmSheet",
)),
)
],
),
),
Align(
alignment: Alignment.bottomCenter,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (_showBottomSheet)
SizedBox(
height: 100,
child: GridView.count(
crossAxisCount: 4,
physics: NeverScrollableScrollPhysics(),
children: [
...List.generate(
8,
(index) => Container(
color: Colors.pink,
))
],
),
),
Container(
width: constraints.maxWidth,
color: Colors.deepPurple,
height: kToolbarHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
...List.generate(
4,
(index) => ElevatedButton(
onPressed: () {
print("tapped on $index");
},
child: Text("$index"),
),
)
],
),
)
],
),
)
],
);
},
));
}
}

Pull down in a flutter app to refresh the app state

I want to update/refresh the balanceAvailable value inside the Text Widget by pulling down the mobile screen in a flutter app.
I have attached a sample code that I am working on for your reference.
I does not seem to work as intended. I would be grateful if someone can provide a solution to this issue.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
GlobalKey<RefreshIndicatorState>();
double balanceAvailable = 0.0;
Future<void> _onRefreshing() async {
setState(() async {
balanceAvailable = 100;
print('newbalance : $balanceAvailable');
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: _onRefreshing,
child: Container(
width: double.infinity,
color: Colors.lightBlue,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () async {},
child: Text("Just a Button 1"),
),
SizedBox(
height: 100,
),
Text('$balanceAvailable'),
],
),
)),
),
);
}
}
In order for RefreshIndicator to work, it needs a vertically scrollable descendant like a ListView:
RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: _onRefreshing,
child: ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: [
Container(
width: double.infinity,
color: Colors.lightBlue,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () async {},
child: Text("Just a Button 1"),
),
SizedBox(
height: 100,
),
Text('$balanceAvailable'),
],
),
),
],
),
)

Flutter: How to show and hide the Lottie animation

I already success to show the Lottie like below:
But the question is, how to trigger to show and hide the Lottie with a button? For example, the Lottie is not showing, but when I clicking Show Lottie button, the Lottie will show, and when I clicking Hide Lottie button, the Lottie will hide.
This is my full code:
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Lottie Flutter'),
),
body: Container(
width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
width: 50,
height: 50,
child: Lottie.asset(
'assets/10219-notification-dot.json',
),
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
onPressed: () {},
child: Text('Show Lottie'),
),
RaisedButton(
onPressed: () {},
child: Text('Hide Lottie'),
),
],
),
],
),
),
),
);
}
}
use a showDialogue method.
child -> GestureDetector, which when tapped do a pop.
Code is below:
showDialog(
context: context,
builder: (context) {
return GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Lottie.asset('assets/animations/success.json',
repeat: false, animate: true),
);
});
});
I solved this with using setState and Visibility widget, this is my code:
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var _isShow = false;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Lottie Flutter'),
),
body: Container(
width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
width: 50,
height: 50,
child: Visibility(
visible: _isShow,
child: Lottie.asset(
'assets/10219-notification-dot.json',
),
),
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
onPressed: () {
setState(() {
_isShow = true;
});
},
child: Text('Show Lottie'),
),
RaisedButton(
onPressed: () {
setState(() {
_isShow = false;
});
},
child: Text('Hide Lottie'),
),
],
),
],
),
),
),
);
}
}
You can use a bool and if else condition to show it. Below code can give an idea perhaphs.
var show = true;
...
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
....
show ?? Lottie.asset(
'assets/10219-notification-dot.json',
),
....
RaisedButton(
onPressed: () {
setState(){show = true}
},
child: Text('Show Lottie'),
),
RaisedButton(
onPressed: () {
setState(){show = false}
},
child: Text('Hide Lottie'),
),

Fixed buttons between AppBar and SingleChildScrollView (Flutter)

I would like to include buttons between AppBar and ListView. In the example below, the buttons scroll along with the text. I tried to include the SingleChildScrollView within a Column, but was unsuccessful.
I read that the Column widget does not support scrolling. I already searched a lot, but I didn't find an example similar to what I need.
Can someone help me?
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('A Idade do Lobo'),
elevation: 0.0,
backgroundColor: COLOR_MAIN,
),
body: NotificationListener(
onNotification: (notif) {
if (_hasScroll) {
if (notif is ScrollEndNotification && scrollOn) {
Timer(Duration(seconds: 1), () {
_scroll();
setState(() {
_controlButton();
});
});
}
}
return true;
},
child: SingleChildScrollView(
controller: _scrollController,
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Center(
child: new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new RaisedButton(
onPressed: _showScrollPickerDialog,
child: Text('Rolagem ${_scrollSpeed}'),
),
new RaisedButton(
onPressed: _showTomPickerDialog,
child: Text('TOM ${_tom}'),
),
],
),
),
new Flexible(
fit: FlexFit.loose,
child: new ListView.builder(
shrinkWrap: true,
itemCount: _songDetails.length,
itemBuilder: (context, index) {
return new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: new EdgeInsets.all(5.0),
child: new RichText(
text: TextSpan(children: [
new TextSpan(
text: '${_songDetails[index].line}',
style: _getStyle(
_songDetails[index].type,
),
),
]),
),
),
],
);
},
),
),
],
),
),
),
floatingActionButton: _controlButton(),
);
}
}
You can use bottom properly of AppBar to achieve desire UI.
Following example clear your idea.
import 'package:flutter/material.dart';
class DeleteWidget extends StatefulWidget {
const DeleteWidget({Key key}) : super(key: key);
#override
_DeleteWidgetState createState() => _DeleteWidgetState();
}
class _DeleteWidgetState extends State<DeleteWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("your title"),
bottom: PreferredSize(
preferredSize: Size(MediaQuery.of(context).size.width, 40),
child: Center(
child: new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new RaisedButton(
onPressed: () {},
child: Text('Rolagem '),
),
new RaisedButton(
onPressed: () {},
child: Text('TOM '),
),
],
),
),
),
),
body: Container(
child: ListView.builder(
itemBuilder: (context, int index) {
return Text(index.toString());
},
itemCount: 100,
),
));
}
}