I have created a Miniplayer in Flutter which looks like this:
Miniplayer(
controller: miniplayerController,
minHeight: 60,
onDismissed: (){
context.read(selectedVideoProvider).state = null;
},
maxHeight: size.height - topPadding - 60,
builder: (height, percentage){
if (selectedVideo == null) {
return const SizedBox.shrink();
}
if (height <= size.width / 16 * 9){
return Row(
children: [
Text('This is a miniplayer'),
IconButton(
onPressed: (){
context.read(selectedVideoProvider).state = null;
},
icon: Icon(Icons.close)
)
],
);
}
return AnotherScreen();
}
);
Also as you can see I have a selectedVideoProvider StateProvider (I use flutter_riverpod for a state management). Also I wrapped my Miniplayer with a Visibility widget:
Consumer(
builder: (context, watch, _){
final selectedVideo = watch(selectedVideoProvider).state;
return Visibility(maintainState: true, visible: selectedVideo != null, child: MyMiniplayerWidget());
}
),
Here is the screen of the app:
Miniplayer appears when I click on these pictures in my ListView. When I click for the first time everything works great (miniplayer appears properly). However when i dismiss my miniplayer I can not open a new miniplayer by clicking on another picture. Only when i go to another tab (tabs are on my bottom navigation bar) and then go back to this screen the miniplayer appears with the MIN size:
But I want it to pop up when I click on the pictures. And again, when I click on the picture for the first time everything works great.
What is wrong with my code? Why miniplayer appears only when I toggle between tabs?
PS. if I make maintainState = false in my Visibility widget miniplayer appears immediately after clicking on the picture, however it appears with the MIN size, but I want it to appear in MAX size. Could you please also explain me why it doesn't work, if I make
context.read(miniPlayerControllerProvider).state.animateToHeight(state: PanelState.MAX);
this when I click on the picture (this is a StateProvider for my miniplayerController) (this line of code makes my miniplayer appear with MAX size when I click on the picture when maintainState = true in Visibility widget)?
I had the same problem with miniplayer and solved it with the following code:
Miniplayer(
valueNotifier: ValueNotifier(MediaQuery.of(context).size.height),
Related
I have a button on every item on my SliverList. When I click a specific list item button, I wish it change to a different widget by using setState. Only that specific item button should change to a different widget while the rest on the list retains its own item button.
Example below. The problem of course is that when I click any button on the list, all buttons on every item on the list changes. What is needed so that it affects only the specific item on the list whose button was pressed?
SliverList(
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
final item = gd[index];
return Container(
child: Column(
children: [
item.mypicwidget(context,item.pid),
_shownewwidget
? item.newwidget(context)
: RaisedButton(
onPressed: () {
setState(() {
_shownewwidget=true;
});
},
child: const Text('Press Me'),
),
]
),
);
Figured this out.
I believe the approach is to add to the existing list an object key to represent whether widget1 or widget2 to render, eg. tag: 'normal' or tag: 'clicked'. Depending on index.tag, either widget1 or widget2 will be displayed. So for a widget with a button wherein the tag is normal, the button will have a function when clicked changes the index.tag of the item from normal to clicked. When the Sliverlist sees this new value, it automatically renders the corresponding widget.
I am trying to stop a gesture from continuing if a certain criteria is met.
The scenario is the user is swiping a tab bar and if they go to the next tab and the condition is true it should disable further swiping. I have tried to put the tab bar in a stack with an Absorb pointer container on top but if they don't let go of the original gesture (i.e. they got to the new tab but didnt let go of the screen) it still allows them to drag through it.
is there anyway to stop the original swipe gesture?
i found this cancel method but i have no idea how to access it
https://api.flutter.dev/flutter/gestures/Drag/cancel.html
In your case there is no need to handle gesture out of the TabBarView simply change the ScrollPhysics.
Here a code example:
final isDisable = ValueNotifier(false);
tabController.animation?.addListener(() {
//The scrolling will be disabled if the second tab is displayed so you can
//add your own logic, may be just checking tabController.index
if (tabController.animation!.value == 1.0) {
isDisable.value = true;
}
});
return Scaffold(
body: ValueListenableBuilder(
valueListenable: isDisable,
builder: (context, bool value, child) => TabBarView(
controller: tabController,
// changing the physics to NeverScrollableScrollPhysics will disable scrolling
physics: value
? NeverScrollableScrollPhysics()
: AlwaysScrollableScrollPhysics(),
children: children,
),
),
);
Please check the video / gif:
I have a pageview that will make the current tab active. I need to ensure the active tab is always visible even if the user swipes the screen multiple times. It is working from the left to right. But when we try back from right to left it's not behaving as expected.
PageView file
PageView(
controller: pageController,
onPageChanged: (int page) {
_duaWidgetState.currentState.updateBtn(page + 1);
Scrollable.ensureVisible(
_duaWidgetState.currentState.activeBtn.currentContext);
},
children: loaded
TabBarWidget with scroll view file
GlobalKey activeBtn = GlobalKey();
var _selectedTab = 1;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 20),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(widget.numberOfTab,
(index) => tabBarItem(index + 1, widget.numberOfTab, activeBtn)),
),
),
);
}
Container that uses the Key
Container(
key: activeBtn,
margin: EdgeInsets.only(left: 20),
padding: EdgeInsets.symmetric(
vertical: 10,
horizontal: 24,
),
No One answered my Question. But, after a lot of effort and time. I found a workaround to fix this problem. Hope this is a bug and will fix by the flutter team in the coming updates.
Scrollable.ensureVisible(_duaWidgetState.currentState.activeBtn.currentContext);
This function has an optional argument called alignment which can be adjusted to make sure the selected element/button is in the center of the viewport. (In my case horizontally center).
My widgets are horizontally scrollable, so I need to update the page number according to the result from PageView(), Here when I used
alignment: 0
Its working fine from left to right swipes of page view. But with this alignment when i swipe page from right to left to go to previous page in the PageView widget, the ensureVisible is not working like expected. The selected element/button is out of the view. I experimented and found that when I used
aligment: 1
The ensureVisible is working fine from the swipes from right to left but at the same time. when I scroll left to right the same problem occured.
Finally, so I managed to do some workaround to fix this behavior. The solution is to store last page index in a variable and check whether the last page is greater than the new page then alignment: 0 and if the last page less than the new page aligment:1 like that.
PageView(
onPageChanged: (int page) {
if (lastPage < page) {
Scrollable.ensureVisible(
_duaWidgetState.currentState.activeBtn.currentContext,
alignment: -0.0100,
);
} else {
Scrollable.ensureVisible(
_duaWidgetState.currentState.activeBtn.currentContext,
(Perfect For font size 24)
alignment: 1.005,
);
}}
Hope my answer will help someone in the future.
I modified the original ensureVisible() a bit and this works in both directions in my case:
// `contentAnchor` is the global key of the widget you want to scroll to
final context = contentAnchor.currentContext!;
final ScrollableState scrollable = Scrollable.of(context)!;
scrollable.position.ensureVisible(
context.findRenderObject()!,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
I'm building an apps, that contain floatingactionbutton which I made with my own custom, when it pressed it shows 3 menu icons below it.
There are 2 problems:
When the orientation change to landscape, and we press floatingactionbutton it won't show instead of it will show when we press long, continue no 2
In current landscape it shows with long press like I said in no 1, and when we back again to portrait it need long press again to make it show
I've been trying some ways to make it fix, but it still doesn't work.
Here's the code and screenshot
When portrait menu show up
When changing to landscape, menu icons are missing and need long press to make it show
for floatingActionButton
floatingActionButton: OrientationBuilder(
builder: (BuildContext context, Orientation orientation){
return orientation == Orientation.landscape
? _buildMenu(context)
: _buildMenu(context);
},
),
_buildMenu() that call floatingActionButton
Widget _buildMenu(BuildContext context){
final icons = [ Icons.swap_vert, Icons.check_circle_outline, Icons.filter_list ];
var nowOrientation = MediaQuery.of(context).orientation;
var b = Container(
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints){
return OverlayBuilder(
showOverlayTrue: true,
overlayBuild: (BuildContext overlayContext){
RenderBox box = context.findRenderObject() as RenderBox;
final center = box.size.center(box.localToGlobal(const Offset(0.8, 0.8)));
return new Positioned(
top: Offset(center.dx, center.dy - icons.length * 35.0).dy,
left: Offset(center.dx, center.dy - icons.length * 35.0).dx,
child: new FractionalTranslation(
translation: const Offset(-0.5, -0.6),
child: FabIcons(
icons: icons,
),
),
);
},
);
},
),
);}
To make it simple in viewing I put some code on github
OverlayBuilder Class https://github.com/ubaidillahSriyudi/StackOverflowhelp/blob/master/OverlayBuilderClass
Fabicons Class
https://github.com/ubaidillahSriyudi/StackOverflowhelp/blob/master/FabIcons
Thanks so much to someone that could help it
Happens because every time you change orientation, your FloatingActionButton rebuilds and loses state.
You should find a way to save the state of FabIcons widget.
Simple solution :
Constructing FabIcons widget with
icons,
iconTapped;
These variables should be saved in the Parent Widget, so every time you call _buildMenu(context); you pass in those variables
I need help with the animation, when I click on the FAB icon in the first screen the icon will go up with animation and the other screen (shown in second image) should be displayed with animation like curtain. And the fab icon should be set in the app bar just like second image.
Bottom menu should be there in both of the screen just like the screenshots given.
you can do one thing that just wrap floating action button to AnimatedContainer
AlignmentDirectional _ironManAlignment = AlignmentDirectional.bottomCenter;
...
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: AnimatedContainer(
duration: Duration(seconds: 2),
alignment: _ironManAlignment,
child: FloatingActionButton(
onPressed: () {
_flyIronMan();
},
child: Icon(Icons.add),
),
),
like this
when you press button then call this method
void _flyIronMan() {
setState(() {
_ironManAlignment =
AlignmentDirectional(0.0, -0.8); //AlignmentDirectional(0.0,-0.7);
});
}
Just wrap buttons on each page with Hero widget, like that:
Hero(
tag: 'fab',
child: FloatingActionButton()
);
With the design you want to achieve, I suggest you to use sliding_up_panel instead of pushing new screen.
It has a parameter collapsed, in which you can put FloatingActionButton to expand panel on tap, and show the same button inside the expanded panel. Just set panel non-draggable, and it won't expand on swipe.
Please read it's documentation carefully, I'm sure you can achieve the effect you want with this widget.