I'm trying to set up a gallery with a swipe effect similar to tiktok.
This is my initial screen:
When the user swipe the screen, the full-screen photo of the dog should appear like this:
Try tiktoklikescroller package
A vertical fullscreen scroll implementation that snaps in place, similar to the TikTok app.
Example :
import 'package:flutter/material.dart';
import 'package:tiktoklikescroller/tiktoklikescroller.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final List<Color> colors = <Color>[Colors.red, Colors.blue, Colors.yellow];
return MaterialApp(
home: Scaffold(
body: TikTokStyleFullPageScroller(
contentSize: colors.length,
swipeThreshold: 0.2,
// ^ the fraction of the screen needed to scroll
swipeVelocityThreshold: 2000,
// ^ the velocity threshold for smaller scrolls
animationDuration: const Duration(milliseconds: 300),
// ^ how long the animation will take
builder: (BuildContext context, int index) {
return Container(
color: colors[index],
child: Text(
'$index',
style: const TextStyle(fontSize: 48, color: Colors.white),
),
);
},
),
),
);
}
}
Output :
Hope it will be useful
Tiktoklikescroller used to but okay be as of September 2021, It works in the example, but would stop scrolling on element 1 with API data, (I don't know why).
So based on one of the comment, this worked for Senfuka (me):
PageView.builder(
scrollDirection: Axis.vertical,
itemCount: widget.products.length,
itemBuilder: (context, index) {
try {
return ProductPage(
widget.products[index],
isHome: false,
);
} catch (e) {
print(e);
return Container();
}
});
thanks for your suggestion. It´s almost working :)
I tried it, but I am getting an error: A RenderViewport expected a child of type RenderSliver but received a child of type RenderStack.
So according to documentation, it needs to be inside a Sliver. I tried put it inside the SliverPadding where I have the Header. And also tried to place it in an independent SliverToBoxAdapter.
But, I can only scroll the hole screen by dragging up the area of the Header. And then if I scroll down again, the Header and the AppBar never show again.
How can I scroll the hole screen and scroll it back?
SliverPadding(
padding: const EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 5.0),
sliver: SliverToBoxAdapter(
child: Column(
children: [
Container(
height: 200.0,
color: Colors.black12,
child: Text('Header', style: TextStyle(fontSize: 30),),
),
Container(
height: MediaQuery.of(context).size.height,
child: TikTokStyleFullPageScroller(
contentSize: colors.length,
swipeThreshold: 0.2,
// ^ the fraction of the screen needed to scroll
swipeVelocityThreshold: 2000,
// ^ the velocity threshold for smaller scrolls
animationDuration: const Duration(milliseconds: 300),
// ^ how long the animation will take
builder: (BuildContext context, int index) {
return Container(
color: colors[index],
child: Text(
'$index',
style: const TextStyle(fontSize: 48, color: Colors.white),
),
);
},
),
),
],
),
),
),
Related
Why my bottomsheet is disappearing upon opening keyboard while clicking textfeild? I tried many solutions but none of them is working properly.
I have nothing much to add just trying to complete the words criteria.
"It looks like your post is mostly code; please add some more details".
Container(
padding: EdgeInsets.symmetric(
horizontal: deviceSize.width * 0.1),
child: InkWell(
onTap: () {
showAddressBottomSheet(context);
},
child: ...// Button style
),
void showAddressBottomSheet(BuildContext _context) {
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: _context,
isScrollControlled: true,
elevation: 20,
builder: (context) {
return AddAddressView(
callback: (val) => setState(() => _addrSelectedTitle = val),
);
},
);
}
Actual Buttom sheet
class AddAddressView extends StatefulWidget {
#override
_AddAddressViewState createState() => _AddAddressViewState();
}
class _AddAddressViewState extends State<AddAddressView> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(30),
child: ListView(
shrinkWrap: true,
children: <Widget>[
Text(......),
TextField(
autofocus: true,
style: TextStyle(fontSize:15),
controller: widget.controller,
..........
),
),
),
),
},
}
I have seen this issue before and since you have posted a reproduction of your actual code, I'm assuming this padding value exists somewhere in your widget tree
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
In older versions of flutter, this padding was required for the bottom sheet to move up when the keyboard is in view. They have however been fixed and you don't need to apply the padding anymore and the framework will handle it. Otherwise there will be twice as much padding as seen in your case.
I would like my app have a model bottom sheet. The bottom sheet is shown only when user click a button. The bottom sheet first take up around 0.5 or less of the screen which is enough to show popular choices from a listview. User can pick their choice right from here but they also can drag up to view all the choices. The bottom sheet can only be either half or full screen. Once it go full screen, I expect it behave like a scaffold (user can scroll the list view but can not drag down to a bottom sheet anymore). How can I do it in flutter?
When user drag the bottom sheet up, it turn into a scaffold like the screen on the right.
[UPDATED]
Another slide panels, we_slide:
import 'package:we_slide/we_slide.dart';
final _colorScheme = Theme.of(context).colorScheme;
final double _panelMinSize = 70.0;
final double _panelMaxSize = MediaQuery.of(context).size.height / 2;
return Scaffold(
backgroundColor: Colors.black,
body: WeSlide(
panelMinSize: _panelMinSize,
panelMaxSize: _panelMaxSize,
body: Container(
color: _colorScheme.background,
child: Center(child: Text("This is the body 💪")),
),
panel: Container(
color: _colorScheme.primary,
child: Center(child: Text("This is the panel 😊")),
),
panelHeader: Container(
height: _panelMinSize,
color: _colorScheme.secondary,
child: Center(child: Text("Slide to Up ☝️")),
),
),
);
OR
bottom_sheet:
showFlexibleBottomSheet(
minHeight: 0,
initHeight: 0.5,
maxHeight: 1,
context: context,
builder: _buildBottomSheet,
anchors: [0, 0.5, 1],
isSafeArea: true,
);
Widget _buildBottomSheet(
BuildContext context,
ScrollController scrollController,
double bottomSheetOffset,
) {
return Material(
child: Container(
child: ListView(
controller: scrollController,
...
),
),
);
}
[OUTDATED]
Try this package sliding_sheet:
return SheetListenerBuilder(
// buildWhen can be used to only rebuild the widget when needed.
buildWhen: (oldState, newState) => oldState.isAtTop != newState.isAtTop,
builder: (context, state) {
return AnimatedContainer(
elevation: !state.isAtTop ? elevation : 0.0,
duration: const Duration(milliseconds: 400),
child: child,
);
},
);
I am trying to create an app with a scroll view and the objects are clickable like the google news app. Can anyone answer how to animate the container to have a white glow on holding the tile?
Here is the list view builder I have for the app
Container(
padding: EdgeInsets.only(top: 16),
child: ListView.builder(
physics: ClampingScrollPhysics(),
itemCount: article.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) {
return news_tile(
imageurl: article[index].urlToimage,
news_title: article[index].title,
news_desc: article[index].description,
web_url: article[index].url
);
}),
)
and this is the contents of the tile which the list view builder calls
class news_tile extends StatelessWidget {
String imageurl, news_title, news_desc,web_url;
news_tile({this.imageurl, this.news_title, this.news_desc,this.web_url});
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => article_view(
web_url: web_url,
)
));
},
child: Container(
margin: EdgeInsets.only(bottom: 16),
child: Column(
children: <Widget>[
ClipRRect(borderRadius: BorderRadius.circular(6), child: Image.network(imageurl)),
SizedBox(
height: 8,
),
Text(news_title, style: TextStyle(fontSize: 17,fontWeight: FontWeight.w600)),
SizedBox(
height: 8,
),
Text(news_desc, style: TextStyle(color: Colors.black54))
],
),
),
);
}
}
You could go with the InkWell Widget. It provides a tapping/holding color effect similar to that. Have a look at the official docs here:
https://api.flutter.dev/flutter/material/InkWell-class.html
Note that you need a Material Widget as an ancestor of your InkWell, but the docs explain that more.
Hope it works for you!
Edit: Sorry, since you are working with a Container, Ink is also important for you:
https://api.flutter.dev/flutter/material/Ink-class.html
Check the docs section "The ink splashes aren't visible!" for why that is.
I am trying to implement some custom design in an expasion panel list. Therefore, I wanted to create some kind of animation that animates smoothly from one view (e.g. header) to another view (e.g. full info of the tile) that has other dimensions (obviously, full info will be higher than just the header). This is quite easy to implement with an AnimatedContainer. However, I would need the height of the header widget and the full info widget in order to animate between these two heigths. As these values differ between tiles (other info -> maybe other height) and tracking height via global keys is not my preferred solution, I decided to use the much simpler AnimatedSwitcher instead. However, the behavior of my AnimatedSwitcher is quite strange. At first, the other tiles in the ListView (in my example the button) move down instantly and subsequently the tile expands. Has anyone an idea of how I could implement some code in order to achieve the same animation that I would get from AnimatedContainer(button/other tiles moving down simultaniously with the tile expanding)? Thanks in advance for any advice. Here is my code:
class MyPage extends State {
List _items;
int pos;
#override
void initState() {
pos = 0;
_items = [
Container(
color: Colors.white,
width: 30,
key: UniqueKey(),
child: Column(
children: <Widget>[Text('1'), Text('2')], //example that should visualise different heights
),
),
Container(
width: 30,
color: Colors.white,
key: UniqueKey(),
child: Column(
children: <Widget>[Text('1'), Text('2'), Text('44534'), Text('534534')],
),
)
];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
padding: EdgeInsets.only(top: 100),
children: <Widget>[
AnimatedSwitcher(
duration: Duration(seconds: 1),
transitionBuilder: (child, animation) => ScaleTransition(
child: child,
scale: animation,
),
child: _items[pos],
),
RaisedButton(
child: Text('change'),
onPressed: pos == 0
? () {
setState(() => pos = 1);
}
: () {
setState(() => pos = 0);
})
],
),
);
}
}
The solution was quite simple. Just found out that there exists an AnimatedSize Widget that finds out the size of its children automatically.
I stumbled on this post and since I had a similar problem I decided to create a tutorial here on how to mix AnimatedSwitcher and AnimatedSize to solve this issue. Animations do not happen at the same time but the advantage is that you have full control on the animation provided to the switcher.
I ended up doing this in the end (please note that I'm using BlocBuilder and that AnimatedSizeWidget is a basic implementation of AnimatedSize:
AnimatedSizeWidget(
duration: const Duration(milliseconds: 250),
child: BlocBuilder<SwapCubit, bool>(
builder: (context, state) {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 1000),
child: state
? Icon(Icons.face, size: 80, key: Key("80"))
: Icon(Icons.face, size: 160, key: Key("160")),
);
},
),
),
var isWidgetA = true;
final Widget widgetA = Container(
key: const ValueKey(1),
color: Colors.red,
width: 100,
height: 100,
);
final Widget widgetB = Container(
key: const ValueKey(2),
color: Colors.green,
width: 50,
height: 50,
);
...
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation) {
return SizeTransition(
sizeFactor: animation,
child: ScaleTransition(
child: child,
scale: animation,
alignment: Alignment.center,
),
);
},
child: isWidgetA
? widgetA
: widgetB,
),
I am new to flutter and have to implement a functionality to select correct words from list of alphabets.First of all here is the image which i have to make.
The alphabets and the correct words will come from the server side.What is was thinking is that i will place all the Alphabets in grid view/list view and when user will select words by using gesture on the alphabets . I know that what i am thinking may be wrong.If thats the case then please tell me the correct way to achieve what is given in the image ? Thanks in advance.
After doing some more research I think I can point you in the right direction and I wrote a small demonstration app that should help you so I found a good article explaining why I was having issues nesting widgets that both receive touch input. It is slightly complex but the gist of it is. If multiple touch inputs are detected they are handled in what is called the GestureArena and the child widget always wins. You can define your own GestureFactory and use RawGestureDetector to work around this issue. However, this might be more than you need for your application and in my opinion is the more complicated route. The route I went still involves just GestureDetector obviously you would need to expand on this as it only draws one oval at this time but that should be easy.
Offset position = Offset(0, 0);
bool isTapped = false;
double width = 50;
double height = 50;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.all(30.0),
child: GestureDetector(
child: GridView(
physics: NeverScrollableScrollPhysics(), //Very Important if
// you don't have this line you will have conflicting touch inputs and with
// gridview being the child will win
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
childAspectRatio: 2,
),
children: <Widget>[
...letters
.map(
(letter) => Text(
letter,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.amber,
fontSize: 18,
),
),
)
.toList(),
],
),
onTapDown: (TapDownDetails details) {
//User Taps Screen
print('Global Position: ${details.globalPosition}');
setState(() {
position = Offset(
details.globalPosition.dx - 25,
details.globalPosition.dy - 25,
);
isTapped = true;
});
print(position);
},
onVerticalDragUpdate: (DragUpdateDetails details) {
print('${details.delta.dy}');
setState(() {
height += details.delta.dy;
});
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
print('${details.delta.dx}');
setState(() {
width += details.delta.dx;
});
},
onTapCancel: () {
//User has released finger from screen
//Validate Word??
},
),
),
),
Positioned(
top: position.dy,
left: position.dx,
child: Container(
height: height,
width: width,
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
side: BorderSide(
color: isTapped ? Colors.blue : Colors.transparent,
width: 3.0),
),
),
),
),
],
),
);
}
Flutter Deep Dive: Gestures found this very helpful key to figuring this all out.