How to add the selected element to the page? - flutter

I have a list of news that comes from the API on the screen.
Output code for one card with news(I shortened the code and left only the necessary):
Widget customListTile(Article article, BuildContext context) {
final newsController = Get.put(FavoritesController());
return InkWell(
Container(
child:Row(
textDirection: TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
article.source.name,
style: TextStyle(
color: Colors.white,
backgroundColor: Colors.red,
),
),
IconButton(
onPressed: () {
newsController.addNews(article);
},
icon: const Icon(Icons.favorite_border)),
]
)
),
);
}
There is an icon, when clicked, the addNews() function is called.And there is a controller code where there is this function:
class FavoritesController extends GetxController {
var _news = {}.obs;
void addNews(Article article) {
if(_news.containsKey(article)) {
}
}
get news => _news;
}
I need when clicking on the icon, the news was added to a separate Saved page.
How to write a function for adding news in a controller? And did I write the code correctly? Can you please tell me how this can be implemented?

First of all, you either use
.obs
or
GetxController function update()
There is no need to change your code tho because this will work as well but you're not using GetxController correctly in this case.
Let's focus on the .obs
Move the
Now, make a ListView that is wrapped with Obx(() => ...) which uses the news obs.
Obx(() {
return ListView(
children: Get.find<FavoritesController>().news.map((e) => customListTile(e)).toList(),
);
});
Let's move to the addNews function.
When you add an article use the update function.
if(_news.containsKey(article)) {
_news.update((e) => e[key]=value);
}
Also, move
final newsController = Get.put(FavoritesController());
outside of this function, even though is not necessary, it makes no sense for it to be there.

Related

Pass On Get Results from one class to another - Flutter

The data unLockCard is properly created in the main class where the button is placed.
When I moved the button to a dialog in a different class - the unLockCard is lost. I receive the error message
What is the best way to pass on unLockCard[number] = tarots[0]; into a different widget or class.
Homepage
List<bool> flips = [false, false, false, false];
List tarots = [];
List unLockCard = [];
Widget _buildTarotCard(key, number, title) {
return Column(
children: [
FlipCard(
key: key,
flipOnTouch: true,
front: GestureDetector(
onTap: () {
tarots.shuffle();
key.currentState.toggleCard();
setState(() {
flips[number] = true;
});
unLockCard[number] = tarots[0];
tarots.removeAt(0);
},
Dialog
Widget _showDialog(BuildContext context) {
Future.delayed(Duration.zero, () => showAlert(context));
return Container(
color: Color(0xFF2C3D50),
);
}
void showAlert(BuildContext context) {
List unLockCard = [];
Dialogs.materialDialog(
color: colorTitle,
msg: 'Congratulations, you won 500 points',
msgStyle: TextStyle(color: Colors.white),
title: 'Congratulations',
titleStyle: TextStyle(color: Colors.white),
lottieBuilder: Lottie.asset('assets/lottie/spirituality.json',
fit: BoxFit.contain,
),
dialogWidth: kIsWeb ? 0.3 : null,
context: context,
actions: [
NeumorphicButton(
onPressed: () => Get.toNamed(Routes.DETAILS,
arguments: unLockCard.sublist(0, 4)),
margin: EdgeInsets.symmetric(horizontal: 8.0),
The code is a bit cluttered. You are building a new List unLockCard = []; in the showAlert() method even though you have one already in the HomePage. I suggest you create a CardController class that deals with everything card related and use it in all the views you need. One very elegant way I found is by using the GetX library (https://github.com/jonataslaw/getx). You can declutter your code using a GetView and a GetController (https://github.com/jonataslaw/getx#getview). However, if you don't want to add a new library to your project, you can achieve the same results by having a single point that deals with card actions (i.e. holds a single instance of the unlockCard list and updates it accordingly).

Storing List Variable on change

I am learning Flutter currently and was making a personal finance app. I have the option to bookmark my guides and then view them on the bookmark tab. Right now, I am using a list to simply store names of guides and display them as list tiles.
The issue I am having is that whenever the bookmark list is updated WHILE the app is running, the Bookmarks page loads the right info but then when I close and restart the app, it goes back to it's initial state of being empty. How can I fix it so that the app saves bookmarked tabs?
main.dart
List<String> bookmarked = [];
String introInfo = """ <h1>Introduction!</h1>
<p><strong><em>NOTE: The guides are U.S. specific but most information can be applied in most countries outside the U.S.</em></strong></p>
<p>The guides in this app will teach you the basics of personal finance.</p>
<p>Financial knowledge is something that is invaluable but the U.S. education system does not put much emphasis on it. If you are looking to get into personal finance, you're at the right place.</p>""";
void main() {
runApp(MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => MyApp(),
'/finTable': (context) => FinNav(),
'/disclaimer': (context) => Disclaimer(),
'/intro': (context) => GuideStyle(guideName: 'introduction',guideInfo: introInfo, isFav: bookmarked.contains('introduction'),),
'/budget': (context) => GuideStyle(guideName: 'budget',guideInfo: introInfo, isFav: bookmarked.contains('budget'),),
'/bookmark': (context) => Bookmarks(),
},
theme: ThemeData(fontFamily: 'Raleway'),
));
}
/* I have a stateless widget that shows all pages and navigates to one the user selects */
guidestyle.dart
class GuideStyle extends StatelessWidget {
String guideName;
String guideInfo;
Widget previous;
Widget next;
bool isFav;
GuideStyle({this.guideName,this.guideInfo, this.isFav });//this.next, this.previous});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromRGBO(220, 20, 60, 1.0),
title: Text('Introduction'),
centerTitle: true,
elevation: 10.0,
actions: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(0.0,2.0,50.0,0.0),
child: MyStatefulWidget(isFav: isFav,name: guideName,),
),
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Back'),
textColor: Colors.white,
color: Color.fromRGBO(220, 20, 60, 0.8),
),
),
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: HtmlWidget(
guideInfo,
)
),
),
),
],
));
}
}
class MyStatefulWidget extends StatefulWidget {
bool isFav;
String name;
MyStatefulWidget({Key key, this.isFav, this.name}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: widget.isFav ? Icon(Icons.bookmark, color: Colors.black) : Icon(Icons.bookmark_border),
onPressed: () {
setState(() {
widget.isFav = !widget.isFav;
if(widget.isFav){
bookmarked.add(widget.name);
bookmarked = bookmarked;
}else{
bookmarked.remove(widget.name);
bookmarked = bookmarked;
}
});
},
),
],
);
}
}
As mentioned, the guidestyle.dart updates the list while the app is running but the list is reset when the app is restarted.
I was looking into using sqflite but it seems overkill so I am unsure about my other options. Any help would be appreciated!
You can use the SharedPreferences package or any other method that is able to persist data between app launches. See this for options to persist data.
Options:
Persist data with SQLite (Though you don't want to use it, it is still an option)
Read and write files
Store key-value data on disk(SharedPreferences) - This is the simplest and will probably suit your needs just fine
If you are using SharedPreferences the setStringList method will suit your needs perfectly.
As a side note, the line bookmarked = bookmarked; is useless.
List<String> bookmarked = []; this always init your data empty
First, You need store package shared_preferences or sqflite or etc..
You can find here => https://pub.dev/
and then check data exist.
After, if(hasData) bookmarked = "loaded data" else bookmarked = [];

Remove Image from Widget's list

On my Page, I have a button to take picture. Once that picture is taken, it'll update my Model (it's using Provider ChangeNotifier). And once picture is taken, the Page gets rebuild, and in the Scaffold main I'm build the widgets:
Widget build(BuildContext context) {
return SingleChildScrollView(
// Somewhere in the middle of this
getPicturesSection(),
// Continue with other widgets
)
}
Widget getPicturesSection(BuildContext context) {
var imagesPath = Provider.of<MyModel>(context, listen:false).imagesPath;
var wids = <Widget>[]
// Basically show all the taken pictures
imagesPath.forEach((f) {
wids.add(
Image.file(
File(f)
)
)
})
return Row(children: wids);
}
What I want to do is allow users to delete each image. So I want to add a delete icon below each image:
imagesPath.forEach((f) {
wids.add(
Column(
children: <Widget> [
Image.file(
File(f)
),
IconButton(
onTap: () {
// How do I delete from the very same list that I am using to build this list?
}
),
],
)
)
})
Never mind, I figured out the answer. Since I'm already using ChangeNotifier, I just need to add the function to remove entry from the model, and the changes will propagate downwards.
List<String> imagesPath = new List<String>();
removeRejectionPicturePath(int ind) {
this.imagesPath.removeAt(ind);
notifyListeners(); // This will basically ask all the widgets that is the listener to rebuild the widget tree
}
You can try this !
imagesPath.forEach((f) {
wids.add(
Column(
children: <Widget> [
Image.file(
File(f)
),
IconButton(
icon:Icon(Icons.remove_circle),
onPressed: () {
setState((){
wids.removeAt(imagesPath.values.toList().indexOf(f));
});
}
),
],
)
);
});

In Dart/Flutter, how do I use a variable from a method so I can ouput it to a text field

Hope somebody can help - I hit this dead end a few weeks ago and think that I've tried everything within my limited knowledge.
I've set up a database that works OK - that is I can add data on one screen, review the data and edit the data on another screen. Now I want to sum one of the columns (beef) which I've been able to do as proven in the 'debugPrint' to the console. I want to access this variable 'beefTotal' from the 'sumBeef' method and print show this in a text field in the UI. I just can't manage it though. It just returns null.
Thanks in advance for any help.
import 'package:flutter/material.dart';
import 'package:take_note/utils/database_helper.dart';
class Info extends StatefulWidget {
#override
State<StatefulWidget> createState() => _InfoState();
}
DatabaseHelper helper = DatabaseHelper();
var database = DatabaseHelper();
class _InfoState extends State<Info> {
List beefTotal;
#override
Widget build (BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Beef Info"),
backgroundColor: Colors.lightBlueAccent,
),
body: Container(
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: RaisedButton(
onPressed: (){
sumBeef();
},
),
),
),
Expanded(
child: Center(
child: Text("Total Beef is: £ $beefTotal", style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 30.0,
fontWeight: FontWeight.w400
),),
),
),
],
),
)
);
}
void sumBeef () async {
beefTotal = await database.addBeef();
debugPrint("Total beef: $beefTotal");
}
}
The code below is from a class called DatabaseHelper which the method sumBeef() uses
Future<List<Map<String, dynamic>>> addBeef()async{
Database db = await this.database;
var result = await db.rawQuery("SELECT SUM(beef) FROM $table");
return result;
}
```[enter image description here][1]
[1]: https://i.stack.imgur.com/L46Gj.png
Just call
setState({
});
void sumBeef () async {
beefTotal = await database.addBeef();
setState(() {});
debugPrint("Total beef: $beefTotal");
}
and your good! anytime you make a change you have to call setState method to update the ui (rebuild) in flutters case

AnimatedSwitcher does not animate

I'm trying to make a news section in my app. In this page that's gonna display the news, i want to be able to click anywhere on the page and get the news that is next in my list. So far no problem with that, but i wanted it to have a nice animation so i tried implementing AnimatedSwitcher, but i can't figure out why there is no animation showing.
I tried changing the hierarchy of my code. Putting the gesture detector inside the animated switcher and the other way around. Letting the main container outside or inside of it too. I tried an animation builder that would scale it just in case it wasnt obvious enough but nothing. Tried changing the duration too but that wasn't it.
class ShowNews extends StatefulWidget {
#override
_ShowNewsState createState() => _ShowNewsState();
}
class _ShowNewsState extends State<ShowNews> {
List<News> _news = [
News(title: 'OYÉ OYÉ', desc: 'bla bla bla bla bla'),
News(title: 'another one', desc: 'plus de bout d\'histoire'),
News(title: 'boum', desc: 'attention à l\'accident'),
News(title: 'Lorem ipsum', desc: 'Lorem ipsum in doloris'),
];
int _currentIndex = 0;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
if (_currentIndex < _news.length - 1) {
_currentIndex++;
} else {
_currentIndex = 0;
}
});
},
child: Container(
height: 160,
padding: EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
child: AnimatedSwitcher(
duration: Duration(seconds: 5),
child: ColumnArticle(_news, _currentIndex),
),
),
);
}
}
Everything is working fine but the animation.
Edit: I tried adding a key to make it different but still no animation.
class ColumnArticle extends StatelessWidget {
final List<News> _news;
final int _currentIndex;
ColumnArticle(this._news, this._currentIndex);
#override
Widget build(BuildContext context) {
return Column(
key: ValueKey<int>(_currentIndex),
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
_news[_currentIndex].title,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 20.0,
),
),
SizedBox(
height: 10.0,
),
Text(
_news[_currentIndex].desc,
style: TextStyle(
fontSize: 14.0,
),
),
],
);
}
}
That happens because the AnimatedSwitcher will add an animation anytime it is rebuilt with a different child reference. However, in your widget lifecycle, you are always using a ColumnArticle as a child, thus, not actually swapping any widget type, that's where the ValueKey comes in play.
You can use the index as the reference for the key, but make sure it actually changes, otherwise it won't work and you also need to pass it to your ColumnArticle base widget (super).
So, your ColumnArticle should look like this:
class ColumnArticle extends StatelessWidget {
final List<News> _news;
final int _currentIndex;
ColumnArticle(this._news, this._currentIndex) : super(key: ValueKey<int>(_currentIndex));
...
}
Passing the same type of widget with different attributes will not trigger an animation since they are the same widgets for the framework. It's also mentioned in the description.
If the "new" child is the same widget type and key as the "old" child,
but with different parameters, then AnimatedSwitcher will not do a
transition between them, since as far as the framework is concerned,
they are the same widget and the existing widget can be updated with
the new parameters. To force the transition to occur, set a Key on
each child widget that you wish to be considered unique (typically a
ValueKey on the widget data that distinguishes this child from the
others).
Here is the code from AnimatedSwitcher that checks whether to animate or not:
if (hasNewChild != hasOldChild ||
hasNewChild && !Widget.canUpdate(widget.child, _currentEntry.widgetChild)) {
// Child has changed, fade current entry out and add new entry.
_childNumber += 1;
_addEntryForNewChild(animate: true);
}
This is the static canUpdate method from the framework:
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
To solve this you can set individual keys to your News widgets based on their distinct attributes (eg. text, count, value). ValueKey<T> is just for that.
Column(
children: <Widget>[
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: Text(
'$_count',
// This key causes the AnimatedSwitcher to interpret this as a "new"
// child each time the count changes, so that it will begin its animation
// when the count changes.
key: ValueKey<int>(_count),
),
),
RaisedButton(
child: const Text('Increment'),
onPressed: () {
setState(() {
_count += 1;
});
},
),
])