I'm using Package Confetti 0.6.0 ConfettiWidget() within Card()s generated in a GridView.builder
Upon scroll the animation stops working properly & even vanishes if I scroll too much.
I thought the issue had to do with lazy loading, so I tried two other particle animation widgets:
Particles 0.1.4 and
Animated Background 2.0.0
Both of which worked fine, displaying correct animation - even through scrolling.
-> It could be just an issue with Package Confetti 0.6.0, but I think the problem might come from the way I'm initializing the ConfettiController and calling .play() method & disposing of it.
Here is my complete simplified code:
class HomePage extends StatefulWidget {
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late ConfettiController _controllerCenter;
#override
void initState() {
super.initState();
_controllerCenter =
ConfettiController(duration: const Duration(seconds: 1000));
_controllerCenter.play();
}
#override
void dispose() {
_controllerCenter.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: SafeArea(
child: new GridView.builder(
itemCount: 30,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Center(
//
child: ConfettiWidget(
confettiController: _controllerCenter,
shouldLoop: true,
),
//
),
);
},
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
),
),
);
}
}
-> I'd like to keep the animation running despite scrolling through the GridView.
I need to use a GridView.builder or GridView.count as data within the cards will be populated with Firebase Data.
Related
I am using the lib pinch_zoom_release_unzoom to pinch zoom image. I create it inside SingleChildScrollView but when user use 2 finger to pinch zoom image. it very hard to zoom because sometime page is Scrollable. so I want to solve this problem
this is my example code
import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:pinch_zoom_release_unzoom/pinch_zoom_release_unzoom.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Tutorial',
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
String imageUrl = 'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg';
TransformationController controller = TransformationController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Tutorial'),
),
body: Column(
children: [
Center(
child: ElevatedButton(
onPressed: () {
showMaterialModalBottomSheet(
expand: false,
context: context,
builder: (context) => PinchZoomReleaseUnzoomWidget(
child: SingleChildScrollView(
controller: ModalScrollController.of(context),
physics: const ClampingScrollPhysics(),
child: Column(
children: [
const SizedBox(
height: 100,
),
Image.network(imageUrl),
const SizedBox(
height: 1000,
),
],
),
),
),
);
},
child: const Text(
'showModalBottomSheet',
),
),
),
],
),
);
}
}
You can interact with physics of scrollable widget to make scrolling different. For that purpose, you should change your physic inside SingleChildScrollView , whenever your zooming state changes. For example:
lass ParentWidget extends StatefulWidget {
const ParentWidget ({Key? key,}) : super(key: key);
#override
State<ParentWidget > createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget > {
late bool isScrolling;
#override
void initState() {
isScrolling = false;
super.initState();
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
physics: isScrolling ? NeverScrollableScrollPhysics() : null,
child: YourWidget(function: (currentState) => setState(() {
isScrolling = currentState;
}));
class YourWidget extends StatelessWidget {
const SceneManaging({Key? key, this.callback}) : super(key: key);
final Function(bool isScrolling)? function;
}
In this case, you should call function inside your child widget whenever you use zoom. When it zooms - pass to it a true, to disable your parent widget scroll, whenever zooms actions stops - pass false.
physics: NeverScrollableScrollPhysics()
How the scroll view should respond to user input.
For example, determines how the scroll view continues to animate after the user stops dragging the scroll view.
Defaults to matching platform conventions. Furthermore, if primary is false, then the user cannot scroll if there is insufficient content to scroll, while if primary is true, they can always attempt to scroll.
To force the scroll view to always be scrollable even if there is insufficient content, as if primary was true but without necessarily setting it to true, provide an AlwaysScrollableScrollPhysics physics object, as in:
physics: const AlwaysScrollableScrollPhysics(),
To force the scroll view to use the default platform conventions and not be scrollable if there is insufficient content, regardless of the value of primary, provide an explicit ScrollPhysics object, as in:
physics: const ScrollPhysics(),
The physics can be changed dynamically (by providing a new object in a subsequent build), but new physics will only take effect if the class of the provided object changes. Merely constructing a new instance with a different configuration is insufficient to cause the physics to be reapplied. (This is because the final object used is generated dynamically, which can be relatively expensive, and it would be inefficient to speculatively create this object each frame to see if the physics should be updated.)
I'm trying to display a list of documents which works, but I read that one good practice is to manage states (which I'm trying currently to understand too). In this case every time I change of screen using the bottomNavigationBar the streamBuilder executes (I always see the CircularProgressIndicator).
I tried call the collection reference in the intState but still the same issue, my code:
class Deparments extends StatefulWidget {
Deparments({Key? key, required this.auth}) : super(key: key);
final AuthBase auth;
#override
_DeparmentsState createState() => _DeparmentsState();
}
class _DeparmentsState extends State<Deparments> {
late final Stream<QuerySnapshot<Object?>> _widget;
Stream<QuerySnapshot<Object?>> getProds(){
CollectionReference ref = FirebaseFirestore.instance.collection("Departamentos");
return ref.snapshots();
}
#override
void initState() {
super.initState();
_widget = getProds();
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: SideMenu(auth: widget.auth),
appBar: AppBar(
title: Text("Departamentos"),
centerTitle: true,
backgroundColor: Colors.green,
),
body: Container(
child: StreamBuilder<QuerySnapshot> (
stream: _widget,
builder: (BuildContext context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
List deparments =
snapshot.data!.docs.map((doc) => doc.id).toList();
return Column(
children: [
Expanded(
child: ListView.builder(
padding: EdgeInsets.only(top: 10),
scrollDirection: Axis.vertical,
itemCount: deparments.length,
itemBuilder: (context, index) {
return SingleChildScrollView(
child: Card(
child: Text(deparments[index]),
),
);
}),
)
],
);
}
}),
),
);
}
}
Update: for those who are facing the same issue Tayan provides a useful solution and he has a video showing the solution https://stackoverflow.com/a/64057210/9429407
Init state will not help you to avoid rebuilds because on changing tabs Flutter rebuilds your Screen. So we need some way to keep our screen alive, so here comes AutomaticKeepAliveClientMixin.
class _HomeState extends State<Home> with AutomaticKeepAliveClientMixin<Home> {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
//Make sure to include the below method
super.build(context);
return SomeWidget();
}
}
The above implementation keeps all of your tab state persists and does not rebuilds the tabs again. Well this may serve your purpose but it may not be idle because this loads all the tabs at once even if the user actually didnt visited a tab, so to avoid the build unless a tab is clicked, use the above method in combination with pageview.
Check out pageView implementation
Also, if you want a better way to manage state and save some of your read calls to Firestore, then you should store data locally and fetch only those needed and/or use paginations.
Initialize your stream in initState just like this answer:
StreamBuilder being called numerous times when in build
multi selection like iOS picture in Flutter
How can I implement an UI with Flutter like the following video?
There are tiles / Rectangles arranged.
They are available for multiple selections.
In this video, I just drug from [B] to [R] (like Picture-1), and [B] to [R] were selected (like Picture-2), even though I did not touch neither [F],[K],[P] nor [E],[J],[O].
what to do with GestureDetector?
And I also wonder whether this is possible with rectangle painted on Canvas?
you can try this, drag_select_grid_view:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final controller = DragSelectGridViewController();
#override
void initState() {
super.initState();
controller.addListener(scheduleRebuild);
}
#override
void dispose() {
controller.removeListener(scheduleRebuild);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: SelectionAppBar(
selection: controller.value,
),
body: DragSelectGridView(
gridController: controller,
itemCount: 20,
itemBuilder: (context, index, selected) {
return SelectableItem(
index: index,
selected: selected,
);
},
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 80,
),
),
);
}
void scheduleRebuild() => setState(() {});
}
When the user opens the app for the first time i would like to explain some features. To do so i designed a bottom sheet which appears in the bottom part of the screen. At the same time i want to darken most of the screen, with the exception of the container I want to explain.
I attached a example picture below...
Thanks for any help!
class Feature extends StatefulWidget {
#override
_FeatureState createState() => _FeatureState();
}
class _FeatureState extends State<Feature> {
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (isFirstOpen)
showBottomSheet(context: context, builder: (context) => BottomInfo());
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
itemBuilder: (context, index) => Container(
padding: EdgeInsets.fromLTRB(50, 30, 50, 30),
),
itemCount: 3,
));
}
}
Update: OverlayEntry helped me to create those hints.
with the following example code, is get a very ugly animation.
I would even say, it's no animation at all.
The next Page will just appear after the setstate is called.
How can I create a smooth delete animation using PageView?
If it is not possible via PageView, is there any alternative, that has the "snapping cards" feature?
Here is my code:
class SwipeScreen extends StatefulWidget {
const SwipeScreen({Key key}) : super(key: key);
static const routeName = '/swipe';
#override
_SwipeScreenState createState() => _SwipeScreenState();
}
class _SwipeScreenState extends State<SwipeScreen> {
List<String> content = ['one', 'two', 'three', 'four', 'five'];
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView.builder(
scrollDirection: Axis.vertical,
itemCount: content.length,
controller: PageController(viewportFraction: 0.8),
itemBuilder: (context, index) {
return Dismissible(
key: ValueKey(content[index]),
child: Card(
child: Container(
height: MediaQuery.of(context).size.height * 0.8,
child: Text('test'),
),
),
onDismissed: (direction) {
setState(() {
content = List.from(content)..removeAt(index);
});
},
);
},
),
);
}
}
Replacing PageView.builder() with ListView.builder() will create a smoother animation.
Hopefully this is what you're looking for!
Unfortunately, the PageView widget is not intended to be used with the Dismissible widget as the animation when the dismiss is complete is not implemented.
You can still change your PageView to a ListView and set a physics to PageScrollPhysics() to get the animation on dismiss but you will probably encounter some other issues on Widget sizes