I want to pass the title data from one screen (ChatModel.dart) to another one.
I created a Model that includes the topic, which in this case is the title of the screen.
This is the model code:
class User {
final int id;
final String topic;
final String imageUrl;
final bool isThereNewMessages;
User({
this.id,
this.topic,
this.imageUrl,
this.isThereNewMessages,
});
}
final User Members = User(
id: 0,
topic: 'New Members',
imageUrl: 'https://...',
isThereNewMessages: true,
);
final User Sell = User(
id: 1,
topic: 'Sell and Buy',
imageUrl: 'https://i....',
isThereNewMessages: true,
);
What I need to do is to access dinamically to the topic in each screen.
I've created a variable to access to the User model:
final chat = User;
and imported the file.
Then I am accessing by doing this:
Text(
User.topic,
textScaleFactor: 1.5,
style: TextStyle(fontWeight: FontWeight.bold),
),
Its not working. How should I pass the data?
You are accessing the User constructor instead of the Members variable.
Change
final chat = User;
To
final chat = Member;
Then
Text(
chat.topic,
textScaleFactor: 1.5,
style: TextStyle(fontWeight: FontWeight.bold),
),
Related
I have this Account class
import 'package:project/models/category_model.dart';
enum AccountTypes {
cash,
banks,
}
class Account {
AccountTypes type;
double value;
List<BalnceCategory>? categories;
Account({
required this.type,
required this.value,
this.categories,
});
Map<String, dynamic> toJSON() {
return {
"type": type,
"value": value,
"categories": categories,
};
}
}
Map<AccountTypes, List<dynamic>> accounts = {
AccountTypes.cash: [
BalnceCategory(image: "food.png", title: "Food", value: 412.5).toJSON(),
BalnceCategory(image: "shopping.png", title: "Shopping", value: 412.5).toJSON(),
],
AccountTypes.banks: [
BalnceCategory(image: "food.png", title: "Food", value: 1242.63).toJSON(),
BalnceCategory(image: "shopping.png", title: "Shopping", value: 1242.63).toJSON(),
]
};
each Account should contain a list of BalnceCategory
class BalnceCategory {
String image;
String title;
double value;
BalnceCategory({
required this.image,
required this.title,
required this.value,
});
Map<String, dynamic> toJSON() {
return {
"image": image,
"title": title,
"value": value,
};
}
}
Now I want to display this Map Map<AccountTypes, List<dynamic>> accounts in two sections...I will refer to this map as accounts.
So in the first section I want to list all available accounts in something like a Row with a button for each account, so what I did is I mapped through accounts like this accounts.entries.map and returned a button for each account, and these buttons can set a state called currentIndex with it's index.
Now in the second section I want to list all accounts categories depending on the currentIndex state value, so for example if the currentIndex value is 0 I want to display all the categories in cash account, and if the currentIndex value is 1 I want to display all the categories in banks account.
So far all I am done the buttons section and I it is working properly and my problem is in the second section. I tried to do this
Expanded(
child: GridView.builder(
physics: const BouncingScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: mainUnit / 2,
crossAxisSpacing: mainUnit / 2,
childAspectRatio: 3 / 4,
),
itemCount: accounts.keys.length,
itemBuilder: (context, index) {
return accounts.forEach((key, value) {
if (key.index == currentIndex) {
value.map((e) => {Text(e.toString())});
}
});
},
),
),
but it gives me this error: The return type 'void' isn't a 'Widget', as required by the closure's context.
The ItemBuilder should return a widget, you return accounts.forEach(...) that is a void function ( forEach() is a void function/closure).
Try this:
Text( accounts.keys.firstWhere( (item) => item.index == currentIndex,
orElse: ()=> "",).toString() );
BUT!!!!
Wait a moment!!!!!
Why don't you take a look at
https://pub.dev/packages/flutter_sticky_header
You should implement what you need: it displays an header that, in your case, could be a string AccountTypes.cash.toString() or AccountTypes.banks.toString(), and then follow the example and you should obtain what you need ( display grouped cards for AccountTypes ). more over I suggest you to use
https://pub.dev/packages/freezed
It helps you to defined your data class, and to Serialize then to JSON.
For Example:
import 'package:freezed_annotation/freezed_annotation.dart';
part 'account.g.dart';
part 'account.freezed.dart';
#freezed()
class Account with _$Account {
factory Account.cash({
#Default(0) double? value;
List<BalnceCategory>? categories;
}) = CashAccount;
factory Account.bank({
#Default(0) double? value;
List<BalnceCategory>? categories;
}) = BankAccount;
factory Account.fromJson(Map<String, dynamic> json) =>
_$AccountFromJson(json);
}
In that manner you have your data class ( account ) with its type ( cash / bank ) and serializable.
To create an account it is easy :
var myCash = Account.cash(... , ... );
Both CashAccount and BankAccount are two different classes that implement Account ( abstract class ), you can use it in a simple list of Accounts.
then , to chack for bank/cash you can use:
var myAccount = Account.cash(....);
myAccount.when(
bank: (BankAccount account) => ....,
cash: (CashAccount account) => .... ,
),
The calendar is working fine, but the problem is whenever I change the month the events aren't visible on the calendar dates. but it appears after I click on any date. can anyone explain what is going wrong here?
Full Calendar Widget implementation
Calendar(
initialDate: month2,
startOnMonday: false,
onMonthChanged: (m) => {
print("onMonthChanged called: " + m.month.toString()),
print(month2),
if (m.month != month2.month)
{
setState(() {
month2 = m;
year2 = m.year;
eventsMap();
}),
events.clear(),
}
},
selectedColor: Colors.blue,
todayColor: Colors.red,
eventColor: Colors.green,
eventDoneColor: Colors.amber,
bottomBarColor: Colors.deepOrange,
events: events,
isExpanded: true,
dayOfWeekStyle: TextStyle(
fontSize: 12,
color: Colors.blueGrey,
fontWeight: FontWeight.bold,
),
bottomBarTextStyle: TextStyle(
color: Colors.white,
),
hideArrows: false,
weekDays: const ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
)
I'm using a calendar widget that displays events on dates in a dot form and also in a form of a list just below it. When I change the month it triggers an onMonthChanged event and fetches the events to display. The only problem I'm having is that Initially, the events aren't showing on dates but after I click on any date, then it shows the events. So, in short, the problem is events aren't displayed initially.
onMonthChanged property implementation
onMonthChanged: (m) => {
print("onMonthChanged called: " + m.month.toString()),
if (m.month != month2)
{
setState(() {
month2 = m.month;
year2 = m.year;
eventsMap();
}),
events.clear(),
}
},
Calendar class
class Calendar extends StatefulWidget {
final ValueChanged<DateTime>? onDateSelected;
final ValueChanged<DateTime>? onMonthChanged;
final ValueChanged<bool>? onExpandStateChanged;
final ValueChanged? onRangeSelected;
final ValueChanged<CleanCalendarEvent>? onEventSelected;
final bool isExpandable;
final DayBuilder? dayBuilder;
final EventListBuilder? eventListBuilder;
final bool hideArrows;
final bool hideTodayIcon;
final Map<DateTime, List<CleanCalendarEvent>>? events;
final Color? selectedColor;
final Color? todayColor;
final String todayButtonText;
final Color? eventColor;
final Color? eventDoneColor;
final DateTime? initialDate;
final bool isExpanded;
final List<String> weekDays;
final String? locale;
final bool startOnMonday;
final bool hideBottomBar;
final TextStyle? dayOfWeekStyle;
final TextStyle? bottomBarTextStyle;
final Color? bottomBarArrowColor;
final Color? bottomBarColor;
final String? expandableDateFormat;
}
provide Date and Time object to
initialDate
parameter
you are using on month changed callback so you have to use onselecteddate callback
This is my model:
class Foo {
final String name;
final String emailId;
Foo(this.name, this.emailId);
factory Foo.fromJson(Map<String, dynamic> json) {
return Foo(
json['name'] as String,
json['email_id'] as String,
);
}
}
I want to create its object by letting user enter text in the TextField, for that I decided to use Map.
Map<String, dynamic> map = {};
My first text field:
TextFormField(
onSaved: (s) => map['name'] = s,
)
and second:
TextFormField(
onSaved: (s) => map['email_id'] = s,
)
I'm creating an instance using:
final foo = Foo.fromJson(map);
Is there any better way of creating an object without modifying my model class (final fields as well as non-nullable fields) except for creating various fields to store the value and then calling Foo() constructor.
There is many ways to solve this, my recommendation is first to load the data, and then modify anything in the ui without touching the class.
First define your data:
// Example fetch fooFromJson(String)
final Foo foo = Foo(name: 'name', email: 'stack#overflow.com');
// Text controllers
late final TextEditingController _controllerName;
late final TextEditingController _controllerEmail;
// Form key
final formKey = GlobalKey<FormState>();
Define your #initState method:
#override
void initState() {
_controllerName = TextEditingController(text: foo.name);
_controllerEmail = TextEditingController(text: foo.email);
super.initState();
}
This way, the data will fetch from the backend just one time, and will show data to the UI without touching the class.
Then in your body widget:
return Form(
key: formKey,
child: ListView(
padding: const EdgeInsets.all(100),
shrinkWrap: true,
children: [
TextFormField(
controller: _controllerName,
//validator: myValidator,
),
TextFormField(
controller: _controllerEmail,
//validator: myValidator,
),
const SizedBox(height: 20),
ElevatedButton(child: const Text('Save'), onPressed: _onSaved),
],
),
);
When you click save button, set the data to your class again (if you don't want to put the data again to the class go to the end of the answer):
_onSaved() {
if (formKey.currentState!.validate()) {
foo.name = _controllerName.text;
foo.email = _controllerEmail.text;
}
}
Result:
On the other hand, Form is use to validate data, for example you want "Text should be 10 characters maximum":
TextFormField(
controller: _controllerName,
validator: _nameValidator,
autovalidateMode: AutovalidateMode.onUserInteraction,
)
And the function:
String? _nameValidator(String? text) {
return text!.length < 10 ? 'Name should have 10 characters at least' : null;
}
Result when you click save:
The data will not be saved until validation is correct.
Also if you want to fetch data from the internet or database, I recommend to improve your data class, for example you have the following Json:
{
"name" :"",
"email" : ""
}
The class should be:
import 'dart:convert';
Foo fooFromJson(String str) => Foo.fromJson(json.decode(str));
String fooToJson(Foo data) => json.encode(data.toJson());
class Foo {
Foo({
this.name,
this.email,
});
String name;
String email;
factory Foo.fromJson(Map<String, dynamic> json) => Foo(
name: json["name"],
email: json["email"],
);
Map<String, dynamic> toJson() => {
"name": name,
"email": email,
};
}
So you can POST foo.toJson() to whatever API you have.
This is a good tool that can help you with that https://app.quicktype.io/
I am trying to make a wats-app like link preview feature , it has two parts ,
Detect URL from text field
Showing preview of that URL
Part 2 has so many plugins to show the preview , but I am stuck with part 1 , how to detect and parse a URL on user typing on textfield .
Also is there a plugin serving both ?
Detect URLs in String/Paragraph and convert them in links in DART:
//
String convertStringToLink(String textData) {
//
final urlRegExp = new RegExp(
r"((https?:www\.)|(https?:\/\/)|(www\.))[-a-zA-Z0-9#:%._\+~#=]{1,256}\.[a-zA-Z0-9]{1,6}(\/[-a-zA-Z0-9()#:%_\+.~#?&\/=]*)?");
final urlMatches = urlRegExp.allMatches(textData);
List<String> urls = urlMatches.map(
(urlMatch) => textData.substring(urlMatch.start, urlMatch.end))
.toList();
List linksString = [];
urls.forEach((String linkText){
linksString.add(linkText);
});
if (linksString.length > 0) {
linksString.forEach((linkTextData) {
textData = textData.replaceAll(
linkTextData,
'<a href="' +
linkTextData +
'" target="_blank">' +
linkTextData +
'</a>');
});
}
return textData;
}
Demo and how to call
String text = "This is my website url: https://github.com/ Google search using: www.google.com, Flutter url: http://example.com/method?param=flutter stackoverflow website url is https://www.stackoverflow.com is greatest website and also check this hashed url https://support.google.com/firebase?authuser=0#topic=6399725";
print(convertStringToLink(text));
Output:
This is my website url: https://github.com/ Google search using: www.google.com, Flutter url: http://example.com/method?param=flutter stackoverflow website url is https://www.stackoverflow.com is greatest website and also check this hashed url https://support.google.com/firebase?authuser=0#topic=6399725
It worked for me, will definitely help my friends :)
I hope that this could help others, talking about step 1:
To detect a URL from the text view, I do the following (taking into consideration that my use case is a chat message in which in the middle of the text could be 1 or several links)
First, having a function that given a String, identify that are URLs:
bool hasURLs(String text) {
const pattern =
r"(https?:\/\/(www.)?|www.)([\w-]+.([\w-]+.)?[\w]+)([\w./?=%-]*)";
final regExp = RegExp(pattern);
return regExp.hasMatch(text);
}
Then a logic to display the text message with a link or without links:
final hasUrls = formatter.hasURLs(stringMessage);
In a widget:
return hasUrls
? UrlMessage(
textContent: messageContents,
textColor: textColor,
isMyMessage: isMyMessage,
)
: Text(
messageContents,
style: TextStyle(color: textColor, fontSize: 13),
);
For UrlMessage widget the code as follows:
class UrlMessage extends StatefulWidget {
const UrlMessage({
Key? key,
required this.textContent,
required this.textColor,
required this.isMyMessage,
}) : super(key: key);
final String textContent;
final Color textColor;
final bool isMyMessage;
#override
State<UrlMessage> createState() => _UrlMessageState();
}
class _UrlMessageState extends State<UrlMessage> {
final formatter = Formatter();
#override
Widget build(BuildContext context) {
final text = widget.textContent;
final textColor = widget.textColor;
final isMyMessage = widget.isMyMessage;
final linkTextStyle = TextStyle(
color: isMyMessage ? Colors.blueGrey : Colors.blue,
fontSize: 13,
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
);
return RichText(
// map each word of the message, ask if it is a URL then set it with each
// TextSpan as a link or not. If it's a link use launchUrl from `url_launcher`
// package to open it
text: TextSpan(
children: text.split(' ').map((word) {
// Check for URLs
if (formatter.hasURLs(word)) {
return TextSpan(
text: word,
style: linkTextStyle,
recognizer: TapGestureRecognizer()
..onTap = () {
// Handle link - here we use `url_launcher`
launchUrl(Uri.parse(word));
},
);
} else {
return TextSpan(
text: '$word ',
style: TextStyle(color: textColor, fontSize: 13),
);
}
}).toList(),
),
);
}
}
Regarding step 2, there are several options to work with the preview, in our case Any Link Preview does what we need
You could try Uri.parse(). Do check the link https://www.geeksforgeeks.org/dart-uris/
Get the value from the textfield Using onChanged function and controller
https://medium.com/flutter-community/a-deep-dive-into-flutter-textfields-f0e676aaab7a
I'm trying to allow a user to mark an item being built by a ListViewBuilder as a favorite. With my current code, when a user favorites one episode, all episodes are marked as favorite. I would like the user to be able to add each episode individually as a favorite and persist that favorite after a restart. I have the data saved to a firebase database but it seems like this should be handled in the app itself.
What is the best way to do this? Thanks!
Here is my current code:
class Epi {
final String endTime;
final String name;
final String networkName;
final String showName;
final String startTime;
Epi({this.endTime, this.name, this.networkName, this.showName, this.startTime});
factory Epi.fromJson(Map<dynamic, dynamic> parsedJson) {
DateTime endTimeCon = DateTime.parse(parsedJson['endTime']);
String newEndTime = formatDate(endTimeCon, [yyyy, '/', mm, '/', dd, ' ', hh, ':', nn, ':', ss, ' ', am]);
DateTime startTimeCon = DateTime.parse(parsedJson['startTime']);
String newStartTime = formatDate(startTimeCon, [yyyy, '/', mm, '/', dd, ' ', hh, ':', nn, ':', ss, ' ', am]);
return Epi(
endTime: newEndTime,
name: parsedJson['name'],
networkName: parsedJson['networkName'],
showName: parsedJson['showName'],
startTime: newStartTime,
);
}
}
bool _isFavorited = true;
void _toggleFavorite() {
setState(() {
if (_isFavorited) {
_isFavorited = false;
} else {
_isFavorited = true;
}
});
}
body: Column(
children: <Widget>[
SizedBox(height: 5.0),
Expanded(
child: ListView.builder(
itemCount: elist.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
selectEpisode(index);
},
child: Card(
child: Column(
children: <Widget>[
ListTile(
title: Text(elist[index].name),
subtitle: Text(elist[index].startTime),
leading: IconButton(
icon: (_isFavorited ? Icon(Icons.favorite_border) : Icon(Icons.favorite)),
color: Colors.red[500],
onPressed: _toggleFavorite,
),
trailing: Icon(Icons.arrow_forward_ios)
)
],
),
),
);
}),
),
],
)
In my Congress Fahrplan App (Github) I'm doing exactly what you want to achieve.
In favorite_provider I store the value in the object itself and add it to my list of favorited objects. Whenever an object is added to this list, the list is written to the disk as JSON with my file_storage class.
When the app is restarted, the objects are fetched from a REST API. Then I match their IDs with the objects from the local JSON and set whether they are favorited or not to restore the favorite state.
Making a favorite list of items basically differs based on the app design and you might as well develop your own logic for this purpose. Now, while what #benjaminschilling33 posted is true, you can also achieve this in a simple way.
What I would do is, add a boolean called isFavorite on the constructor like this:
class Epi {
final String endTime;
final String name;
final String networkName;
final String showName;
final String startTime;
bool isFavorite;
}
//initialize the isFavorite to false cause no item in your list is is favorite at the beginning
Epi({this.endTime, this.name, this.networkName, this.showName, this.startTime, this.isFavorite=false});
//lets create a list _episode which contains all the movie for demonstration purpose
List<Epi> _episode = [Epi(initialized data)];
//create a getter for the list
List<Epi> get episode{
return _episode.where((Epi episode) => episod.isFavorite).toList(); //this will return a list where the isFavorite is true
}
//You can then set your icon (in the list-tile) based on your isFavorite result
ListTile(
...
icon: Icons(elist[index].isFavorite?Icon(Icons.favorite):Icon(Icons.favorite_border);
)
//Then adjust the logic on your onPress
onPressed: (){
setState((){
elist[index].isFavorite=!elist[index].isFavorite //this will vary from true to false and vice versa when pressed
});
}
This is the simplest way to add list of items that is favorited by the user rather than building another list for the favorite section. What I wrote here is offline based test you can achieve and the key take away is the where property which is:
List<Epi> episode=[some data]
epsode.where((Epi episode)=>episode.isFavorite).toList();
You can use this method even after deploying your app to the cloud database by creating that attribute in your database based on the user's id.