How to use the pageview inside show dialog (Flutter)? - flutter

The first section is an automatic page that I want to make in a dialog box with Flutter.
This is the code I used to try it out, but I couldn't get to what I wanted
Please help me with this by showing the pageView and auto indicator in the dialog
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final PageController _controller = PageController(initialPage: 0);
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: const Text("Show Dialog"),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Warning'),
content: PageView(
controller: _controller,
children: [
Container(width: double.infinity, height: double.infinity, color: Colors.yellow),
Container(width: double.infinity, height: double.infinity, color: Colors.red),
Container(width: double.infinity, height: double.infinity, color: Colors.black),
],
),
actionsAlignment: MainAxisAlignment.center,
actions: <Widget>[
Column(
children: [
ElevatedButton(onPressed: () {}, child: const Text("CONTINUE")),
OutlinedButton(onPressed: () {}, child: const Text("NO THANKS"))
],
),
],
),
);
},
),
),
);
}
}

We need to provide size on PageView. Based on your attached image, I am using LayoutBuilder to get the constraints and providing 30% height of the dialog. Use constraints to provide size.
showDialog(
context: context,
builder: (BuildContext context) =>
LayoutBuilder(builder: (context, constraints) {
debugPrint("${constraints.toString()}");
return AlertDialog(
title: const Text('Warning'),
content: Column(
children: [
SizedBox(
height: constraints.maxHeight * .3,
width: constraints.maxWidth,
child: PageView(
controller: _controller,
children: [
Container(color: Colors.yellow),
Container(color: Colors.red),
Container(color: Colors.black),
],
),
),
],
),
actionsAlignment: MainAxisAlignment.center,
actions: <Widget>[
Column(
children: [
ElevatedButton(
onPressed: () {},
child: const Text("CONTINUE")),
OutlinedButton(
onPressed: () {},
child: const Text("NO THANKS"))
],
),
],
);
}));
If you find the content get overflow after adding many widget wrap top column with SingleChildScrollView
showDialog(
context: context,
builder: (BuildContext context) =>
LayoutBuilder(builder: (context, constraints) {
debugPrint("${constraints.toString()}");
return AlertDialog(
title: const Text('Warning'),
content: SingleChildScrollView(
child: Column(
More about LayoutBuilder

Related

How to stack two bottom sheet in flutter?

I want to stack two bottom sheet each other in flutter as show in photo. The upper one is shown when in error state. In photo, it build with alert dialog. I want is with bottom sheet. How can I get it?
Edit:
Here is my code that I want to do. Lower bottom sheet is with pin field, autoComplete. autoComplete trigger StreamController, and then streamBuilder watch Error state and show dialog.
confirmPasswordModalBottomSheet(
BiometricAuthRegisterBloc biometricAuthRegBloc) {
showMaterialModalBottomSheet(
context: context,
builder: (BuildContext context) {
return StreamBuilder(
stream: biometricAuthRegBloc.biometricAuthRegisterStream,
builder: (context,AsyncSnapshot<ResponseObject>biometricAuthRegSnapShot) {
if (biometricAuthRegSnapShot.hasData) {
if (biometricAuthRegSnapShot.data!.messageState ==
MessageState.requestError) {
showModalBottomSheet(context: context, builder:
(BuildContext context){
return Container(
width: 200,height: 200,
child: Center(child: Text('Helllllllllo'),),);
});
}
}
return SizedBox(
width: 100,
height: 300,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: margin30,
),
Text(CURRENT_PIN_TITLE),
SizedBox(
height: margin30,
),
Padding(
padding: const EdgeInsets.only(
left: margin60, right: margin60),
child: PinCodeField(
pinLength: 6,
onChange: () {},
onComplete: (value) {
biometricAuthRegBloc.biometricAuthRegister(
biometricType:_biometricAuthTypeForApi,
password: value);
},
),
),
SizedBox(
height: margin30,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal:
margin80),
child: AppButton(
onClick: () {},
label: CANCEL_BTN_LABEL,
),
),
Container(
padding: const EdgeInsets.all(8.0),
margin:
EdgeInsets.symmetric(vertical: 8.0,
horizontal: 30),
decoration: BoxDecoration(
color: Colors.grey,
border: Border.all(color: Colors.black),
),
child: const Text(
FINGER_PRINT_DIALOG,
textAlign: TextAlign.center,
),
)
],
),
);
});
},
);
}
When I do like that above, I get setState() or markNeedsBuild() called during build. Error and why? Sorry for my previous incomplete question.
I am bit confused with your question but stacking two bottomsheet is just easy. You just need to call the showModalBottomSheet whenever you want it shown to user. You can check out the following implementation:
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ElevatedButton(
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 500,
color: Colors.amber,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet 1'),
ElevatedButton(
child: const Text('Show second modal 2'),
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
color: Colors.redAccent,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet 2'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
),
],
),
),
);
},
);
},
),
],
),
),
);
},
);
},
child: Text('Show bottom sheet 1'),
),
);
}
}
I have solution. All I need to do is, need to add WidgetBinding.insatance.addPostFrameCallback((timeStamp){showModalBottomSheet()}); in the StreamBuilder return.

video playing in background flutter

im working through a scrollable page view somewhat like tic tok and each time i leave the screen the video continuously play in the background, which i dont want, cuz this code ids from a youtube tutorial i dont know how to work around the problem, so please help me check problem for a viable solution
import 'package:app/packageManager/package.dart';
import 'dart:io';
class Reels extends StatefulWidget {
const Reels({Key? key}) : super(key: key);
#override
State<Reels> createState() => _ReelsState();
}
vidPicker(ImageSource src, BuildContext context) async{
final vid = await ImagePicker().pickVideo(source: src);
if(vid != null){
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => VideoUploader(
videoPath: vid.path, videoName:vid.name,
videoFile:File(vid.path)
)));
}
} //To Pick a Video
showDialogueBox(BuildContext, context){
return showDialog(context: context, builder: (context) => SimpleDialog(
children: [
SimpleDialogOption(
onPressed: () => vidPicker(ImageSource.gallery, context),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: const [
Icon(Icons.image),
Text(" Gallery ")
],
),
),
),
SimpleDialogOption(
onPressed: () => Navigator.of(context).pop(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: const [
Icon(Icons.cancel),
Text(" Cancel ")
],
),
),
)
],
));
} //TO Show Option Like Gallery and Cancel
class _ReelsState extends State<Reels> {
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
automaticallyImplyLeading: false,
elevation: 0,
flexibleSpace: SizedBox(
height: 200,
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(onPressed: () => showDialogueBox(BuildContext,context),
icon: const Icon(Icons.add),
iconSize: 35,
color: Colors.teal[300],
)
],
),
),
),
backgroundColor: Colors.transparent,
),
body: StreamBuilder(
stream: fireStore.collection("videoReels").snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
// TODO: Put Progress Bar Here
if (!snapshot.hasData) return const Text("Loading...");
return PageView.builder(
itemCount: snapshot.data!.docs.length,
scrollDirection: Axis.vertical,
controller: PageController(viewportFraction: 1, initialPage: 0),
itemBuilder: (context, index){
DocumentSnapshot dataSnapshot = snapshot.data!.docs[index];
return Stack(
alignment: Alignment.bottomCenter,
children: [
VideoPlayerContent(videoUrl: dataSnapshot["videoUrl"]),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
flex: 3,
child: Container(
height: MediaQuery.of(context).size.height/6,
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
InkWell(
onTap: () => {},
child: const Icon(Icons.thumb_up, color: Colors.blueAccent,),
),
const SizedBox(width: 30,),
InkWell(
onTap: () => {},
child: const Icon(Icons.favorite, color: Colors.red,),
)
],
),
const SizedBox(width: 20,),
InkWell(
onTap: () => {},
child: Text(" Add Comment here... ", style: TextStyle( color: Colors.grey[500] ),),
)
],
),
const SizedBox(height: 30,),
// Second Row
Padding(
padding: const EdgeInsets.fromLTRB(30.0, 4.0,30.0,4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkWell(
onTap: () => { } ,
child:ClipRRect(
borderRadius: BorderRadius.circular(50.0),
child: CircleAvatar(backgroundImage: NetworkImage(dataSnapshot['avatar']), radius: 25,),
),
),
Text((dataSnapshot["videoUrl"])),
ElevatedButton(onPressed: ()=> {}, child: Text("Subscribe"))
],
),
)
],
),
),
),
// TODO: Remove this later
],
)
],
);
},
);
}
),
);
}
}
import 'package:app/packageManager/package.dart';
class VideoPlayerContent extends StatefulWidget {
final videoUrl;
const VideoPlayerContent({Key? key, required this.videoUrl}) : super(key: key);
#override
State<VideoPlayerContent> createState() => _VideoPlayerContentState();
}
class _VideoPlayerContentState extends State<VideoPlayerContent> {
late VideoPlayerController _videoController;
late Future _initializeVideoPlayer;
#override
void initState(){
_videoController = VideoPlayerController.network(widget.videoUrl);
_initializeVideoPlayer = _videoController.initialize();
_videoController.play();
_videoController.setVolume(1);
_videoController.setLooping(true);
super.initState();
}
#override
void dispose (){
_videoController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: FutureBuilder(
future: _initializeVideoPlayer,
builder: (context, snapshot){
if (snapshot.connectionState == ConnectionState.done){
return VideoPlayer(_videoController);
}else{
return Container(
color: Colors.black,
child: const Center(
child: CircularProgressIndicator(
value: 0.8,
valueColor: AlwaysStoppedAnimation<Color>(Colors.purpleAccent),
),
)
);
}
},
),
);
}
}
Have you tried WidgetsBindingObserver and VisibilityDetector?
If you have not, then it might be handy to you.
For WidgetBindingObserver, simply use it as mixin like this:
class _VideoPlayerContentState extends State<VideoPlayerContent> with WidgetsBindingObserver{
#override
void initState(){
WidgetsBinding.instance?.addObserver(this);
super.initState();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
// TODO: Handle this case.
break;
case AppLifecycleState.inactive:
// Do like this in other lifecylestate if required !
_videoController.pause();
// TODO: Handle this case.
break;
case AppLifecycleState.paused:
// TODO: Handle this case.
break;
case AppLifecycleState.detached:
// TODO: Handle this case.
break;
}
super.didChangeAppLifecycleState(state);
}
// rest of the code .....
#override
void dispose(){
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
}
}
Using visibility detector:
simply wrap the video player with this widget like:
VisibilityDetector(
// Must provide key
key: ValueKey<String>('give any string value to represent key'),
onVisibilityChanged: (visibilityInfo) {
// 0 ---> visible, 1 --> not visible
if(visibilityInfo.visibleFraction == 0){
_videoController.pause();
// might need setState over here
}
},
child : VideoPlayer(_videoController)
);

View behind Scaffold - Flutter/Dart

I tried a lot to get the behavior of the iOS project https://github.com/ivanvorobei/SPLarkController working in Flutter / Dart. I do not understand how to get another view behind the scaffold (holding also the bottom navigation bar). Any ideas how this can be achieved?
This could be achieved with the help of Stack.
First layer for the buttons on the bottom:
Second layer for the main content:
Then, you can wrap the BottomNavBar inside GestureDetector with onVerticalDragUpdate property.
Complete Code:
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(
home: Scaffold(
body: Builder(
builder: (context) => MyChild(MediaQuery.of(context).size.height),
),
),
);
}
}
class MyChild extends StatefulWidget {
final double screenHeight;
const MyChild(this.screenHeight, {Key? key}) : super(key: key);
#override
_MyChildState createState() => _MyChildState();
}
class _MyChildState extends State<MyChild> {
double val = 1.0;
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
padding: const EdgeInsets.only(bottom: 20.0),
color: const Color(0xFF303030),
child: Padding(
padding: const EdgeInsets.only(left: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Row(
children: [
ElevatedButton(
onPressed: () {}, child: const Text('Button 1')),
const SizedBox(
width: 20.0,
),
ElevatedButton(
onPressed: () {}, child: const Text('Button 2'))
],
),
const SizedBox(
height: 20,
),
Row(
children: [
ElevatedButton(
onPressed: () {}, child: const Text('Button 3')),
const SizedBox(
width: 20.0,
),
ElevatedButton(
onPressed: () {}, child: const Text('Button 4'))
],
),
],
),
),
),
LayoutBuilder(
builder: (context, constraints) => AnimatedContainer(
duration: const Duration(milliseconds: 500),
curve: Curves.ease,
height: constraints.maxHeight * val,
color: Colors.white,
child: Column(
children: [
Expanded(
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: 25,
itemBuilder: (context, index) => ListTile(
title: Text('ListTile $index'),
),
),
),
GestureDetector(
onVerticalDragUpdate: (details) {
if (details.delta.dy < 0) { // If the user drags upwards
setState(() {
val = 0.7;
});
} else if (details.delta.dy > 0) { // If the user drags downwards
setState(() {
val = 1.0;
});
}
},
// Create your bottom navigation bar here
// and not bottomNavigationBar property of Scaffold
child: Container(
color: Colors.green.shade100,
height: 80,
),
)
],
),
),
),
],
);
}
}

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"),
),
)
],
),
)
],
),
)
],
);
},
));
}
}

Building widgets outside of AlertDialog

Is it possible to build a widget, for example a button, in the barrier area of a dialog? By the barrier area I mean the area out of the bounds of an AlertDialog. For example: an AlertDialog is shown in the middle of the screen and a TextButton is in the bottom-right of the screen outside of shown AlertDialog.
You just call from outside and pass the context
getDialog(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) => Center(
child: AlertDialog(
insetPadding: EdgeInsets.all(20),
contentPadding: EdgeInsets.all(0),
content: Container(
height: 200,
width: MediaQuery.of(context).size.width,
child: Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: TextButton(
child: Text("Close"),
)),
)),
),
),
));
}
Here is the full source code
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:so_test/screen/shop_card.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: "ListView.builder",
theme: new ThemeData(primarySwatch: Colors.green),
debugShowCheckedModeBanner: false,
home: HomeApp(),
);
}
}
class HomeApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: InkWell(
onTap: () {
getDialog(context);
},
child: Center(child: Text("Open Dialog")),
),
),
);
}
}
getDialog(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) => Center(
child: AlertDialog(
insetPadding: EdgeInsets.all(20),
contentPadding: EdgeInsets.all(0),
content: Container(
height: 200,
width: MediaQuery.of(context).size.width,
child: Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: TextButton(
child: Text("Close"),
)),
)),
),
),
));
}
output: