IconButton calling setState during onPressed shows no ripple effect - flutter

During the onPressed of my IconButton I need to update the database and then the UI so that the user sees feedback of the data change. To do this I call setState, which successfully lets the Widget rebuild. The problem is I'm no longer seeing the touch feedback ripple effect because the Widget rebuilds immediately.
var button = new IconButton(
icon: new Icon(isMyBoolTrue ? Icons.undo : Icons.event_available),
onPressed: () => setState(() => toggleDatabaseBool)
);

The issue was I was creating an ObjectKey with an object that was re-created every time during build. Solved the issue by using ObjectKey and the id field of my object instead of the object itself.
Bad
var card = new Card(
key: new ObjectKey(goal), //the goal object was re-created during `build`
child: button
);
Good
var card = new Card(
key: new ObjectKey(goal.id), // need to key off of the id field as it remains constant
child: button
);

That shouldn't stop the splash. The only reason the splash should stop is if you add or remove one of the widgets between the IconButton and the Material, or change its key, or change the Material's key, or move the Material in the tree. (Some of those are bugs in the framework right now, I hope to fix them in the coming months.)
Can you post a minimal app that shows the problem?
Meanwhile, we're tracking the issue at https://github.com/flutter/flutter/issues/6751 and https://github.com/flutter/flutter/issues/5456

Related

Is it fine to use .obs on Widgets after assing to var?

Hey am Wondering is it fine to use ".obs" to widget like this :
var name= Text("foo").obs;
return name.value;
using .obs on whole widgets like this:
Rx<Widget> textWidget = Text("test").obs;
will work fine, as you can try in this exmaple:
// ...
child: Obx(
() => controller.textWidget.value,
),
and every time you assign a value to it, it will update, but this will cuase very unecessary recreating of that Text widget, since each time, a new Text widget will set in your UI, which may cause perferemnce issue.
instead you should declare primitive types as Rx observales then assignig them inside your UI widgets:
final textVal = "test".ons;
child: Obx(
() => Text(controller.textVal.value),
),

Custom Event listeners in flutter

I have a widget with a list and a button with a tree dot icon in every row that shows and hides a panel in its own row. I only want one panel open in the list. When I click on a row button, I'd like to close the panels of the other rows list.  All the buttons in the list are siblings. I'd like to send an event to the other rows' code to close the panels. Which is the correct manner of flutter?  
I have tried NotificationListener but it does not work because the components to be notified are not their parents.
The question is if the correct thing to do is to use the event_listener library or to use streams. I'm new to flutter/dart and streams seem too complex to me. It's a very simple use case and in this entry
Flutter: Stream<Null> is allowed?
they say
*
Some peoples use streams as a flux of events instead of a value
changing over time, but the class isn't designed with this in mind.
They typically try to represent the following method as a stream:
So with simple events with 0 or 1 argument. event_listener or Streams?
This is the screen I'm working on. I want that when one yellow button panel opens the other one closes.
Your question is broad and it seems to be a design question, i.e. it doesn't have a right answer.
However, I don't think you should use Streams or EventListeners at all in this case, because you should not make components in the same layer communicate with each other. Components should only communicate with their parents and children, otherwise your code will increase in complexity really fast. That's even documented in flutter_bloc.
Other than that, if you don't lift state up, i.e. move the responsibility of triggering the removal of the other rows to a parent Widget, than you're fighting against Flutter instead of letting it help you.
It's easy to create a parent Widget, just wrap one Widget around it. What you want to do is hard, so why would try to communicate with sibling widgets instead of using what's Flutter designed to do?
This is a suggestion:
class _NewsSectionState extends State<NewsSection> {
Widget build(BuildContext context) {
return ListView.builder(
itemCount: newsInSection.length;
itemBuilder: (_, int index) => NewsTile(
title: Text('${newsInSection[index].title}')
onDismiss: () => onDismiss(index),
// I don't know how you set this up,
// but () => onDismiss(Index)
// should animate the dismiss of the Row with said index
),
);
}
}
class NewsRow extends StatefulWidget {
final void Function() onDismiss;
#override
State<NewsRow> _createState => _NewsRowState();
}
class _NewsRowState extends State<NewsRow> {
Widget build(BuildContext context) {
return Row(
children: [
// title
// home button
// fav button
// remove button
IconButton(
Icons.close,
onPressed: widget.onDismiss,
),
],
);
}
}

Can a state in flutter call itself?

I have a stateful widget, that gets updated with button press events, is it viable to call the state from inside the same state on button press event? The states are a lot in number. So I want to avoid initializing too many states.
Yes you can call setState((){}) inside a StatefulWidget. According to the documentation:
Notify the framework that the internal state of this object has changed.
That means that, if you want to update any value on your StatefulWidget, make sure to call it inside the setState((){}), like this: setState(() { _myState = newValue; });
From what I understood, you don't want to have too many calls to the setState((){}) function. Since you are using a button, you can do it like this:
FlatButton(
child: Text("Tap me!"),
onPressed: () => setState((){ tapped = true}),
),
The arrows are syntatic sugar that substitute the curly brackets. Useful when there is only one line of code.

How to rebuild the state after pushing a new screen

When i push a new screen onTap with Navigator and pass a new class constructor, how can I have that new screen updates every time _playerTimer updates without having to click again
Since the state of my new class only updates onTap, please help!
The build method of FullScreenDialog is called once since its only being built when onTap is pressed
InkWell(
onTap: () {
return Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => FullScreenDialog(
_playerTimer,
));
},
child: VideoPlayer(
_controller,)
);
you have to use setState to rebuild the UI.
ex:
setState((){
_playerTimer = _playerTimer + 1;
});
that's about all the help I can give without seeing the rest of your code
When you instantiate a new class (in your code would be the FullScreenDialog) by passing an attribute, you're only saying to your code that a new class will be initialized by using the argument you provided.
If you want your FullScreenDialog class to always be updated when _playerTimer changes, you must observe this attribute inside this class by using setState(), which is a build-in function for StatefulWidgets that makes apps' UI update every time the observable attribute changes.
Example:
setState( () {
_playerTimer = getUpdatedTimer();
});
Supposing that inside getUpdatedTimer() method you will manage the logic for updating this variable, calling a service or so.
If you want this variable to be updated without interacting with interface, you probably will need a timer, too. Check this question to more details about it.
If you're starting with Flutter development, I suggest you to read this article (Adding interactivity to your Flutter app) about state management
and
setState method documentation.
Hope that helps.

Flutter: how to access context from Dismissible onDismissed

I'm trying to implement undo for a Dismissible list item in Flutter, and having problems accessing a BuildContext.
I have a flutter list, where each item is a card. The card is wrapped in a Dismissible, which allows the user to swipe to dismiss the card. Dismissible automatically removes the item from the list. Dismissible also has an onDismissed event - I'm using this event to update the item in Redux state store (setting an isDismissed flag to true), then show a snackBar which contains an UNDO button.
This is where I'm running into problems. I want the UNDO button to restore the item, by dispatching another action to the Redux store to set isDismissed to false. To do this I need a context, from which to get the store dispatcher. However when I try with the below code, I get an error when clicking on UNDO:
Looking up a deactivated widget's ancestor is unsafe
class ProductCard extends StatelessWidget {
final Product product;
const ProductCard(this.product);
#override
Widget build(BuildContext context) {
return Dismissible(
key: Key(product.id.toString()),
onDismissed: (direction) {
StoreProvider.of<AppState>(context).dispatch(DismissAction(product));
// Then show a snackbar to allow undo
Scaffold.of(context).showSnackBar(
SnackBar(
content: Row(
children: <Widget>[
Expanded(child: Text("Dismissed ${product.title}"),),
FlatButton(
onPressed: () {
// THIS IS WHERE I GET THE ERROR
StoreProvider.of<AppState>(context).dispatch(UndoDismissAction(product));
},
child: Text("UNDO"),
)
],
)
)
);
},
child: Card(
child: ...
)
);
}
}
From what I've read, I think what is going on is that the line StoreProvider.of<AppState>(context) inside the undo button's onPressed action is trying to use a context which belongs to the Card, but because the card has been removed from the list, it no longer exists.
I'm not sure how to do work around this. I've read about flutter keys, and think the answer may be to start passing around some kind of global key, but I can't quite get my head around how that works. I gave it a go and ran into another problem with 'inheritFromWidgetOfExactType' was called on null. Are keys the solution to this problem? If so where do I create the key, do I pass it in to the widget, what type of key should I use etc, or is there a better solution?
Many thanks!
Extract a single copy of the store into a local variable, which will then get captured by all the lambdas below.
#override
Widget build(BuildContext context) {
var store = StoreProvider.of<AppState>(context);
return Dismissible(
...
store.dispatch(DismissAction(product));