Update the state of navigation bar item in flutter - flutter

I have Navigation page contain navigation bar, one of these items it's friend request and I want show number of friend request, I do that, but I want update the number of friend request of item when the number increasing or decreasing in navigation page.
I'm passing the values of notifications below, so when I try to add a new friend request the notification numbers not change but when I closed app and reopen again the friend requests updated.
body: Stack(
children: <Widget>[
IndexedStack(
children: <Widget>[
FriendRequestes(),
ChatListScreen(),
HomePage),
Tasks(),
Projects()
],
index: _selectedItem,
)
// Streambuilder to update the number of friend request from firestore
StreamBuilder(
stream: _firestore
.collection(USERS_COLLECTION)
.where(UID_FIELD, isEqualTo: widget.me.uid)
.limit(1)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
const Text('Loading...');
} else {
return ListView.builder(
physics:
NeverScrollableScrollPhysics(),
shrinkWrap: true,
scrollDirection:
Axis.vertical,
itemCount: snapshot
.data.documents.length,
itemBuilder:
(context, index) {
DocumentSnapshot userData = snapshot.data.documents[index];
List<String> requested = <String>[];
if(userData.data[REQUESTED_FIELD] != null)
{
List.from(userData.data[REQUESTED_FIELD]).forEach((element){
requested.add(element);
});
}
widget.me = User(
arrayRequested: requested,
);
rebuild(widget.me);
return Container(width: 0.0,height: 0.0,);
});
}
return Container(
height: 0.0,
width: 0.0,
);
},
),
bottomNavigationBar: CustomBottomNavigationBar(
onChange: (val) {
setState(() {
_selectedItem = val;
});
},
defaultSelectedIndex: 2,
width: width,
lang: widget.myAppLang,
iconList: [
"assets/icon/icon_friendReq.png",
"assets/icon/icon_chats.png",
"assets/icon/icon_home.png",
"assets/icon/icon_tasks.png",
"assets/icon/icon_projects.png",
],
textList: [
"Profile",
"Chats",
"Home",
"Tasks",
"Projects",
],
// here I'm passing notification number
notiList: [widget.me.arrayRequested.length, 0, 0, 0, 0],
),
// to update the state of number of friend requests from firestore
rebuild(User user){
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
widget.me = user;
});
});
}
// Navigation Bar
class CustomBottomNavigationBar extends StatefulWidget {
final int defaultSelectedIndex;
final double width;
String lang;
final Function(int) onChange;
final List<String> iconList;
final List<String> textList;
final List<int> notiList;
CustomBottomNavigationBar(
{this.defaultSelectedIndex = 0,
#required this.iconList,
#required this.textList,
#required this.notiList,
#required this.onChange,
#required this.width,
#required this.lang,
});
#override
_CustomBottomNavigationBarState createState() =>
_CustomBottomNavigationBarState();
}
class _CustomBottomNavigationBarState extends State<CustomBottomNavigationBar> {
int _selectedIndex = 0;
double _width;
String _lang;
List<String> _iconList = [];
List<String> _textList = [];
List<int> _notiList = [];
#override
void initState() {
// TODO: implement initState
super.initState();
_selectedIndex = widget.defaultSelectedIndex;
_iconList = widget.iconList;
_textList = widget.textList;
_notiList = widget.notiList;
_width = widget.width;
_lang = widget.lang;
}
#override
Widget build(BuildContext context) {
List<Widget> _navBarItemList = [];
for (var i = 0; i < _iconList.length; i++) {
_navBarItemList
.add(buildNavBarItem(_width, _lang, _iconList[i], _textList[i], _notiList[i], i));
}
return Row(
children: _navBarItemList,
);
}
Widget buildNavBarItem(
double width, String lang, String icon, String text, int n, int index) {
return GestureDetector(
onTap: () {
setState(() {
widget.onChange(index);
_selectedIndex = index;
n = n;
});
},
child: Stack(
children: [
Container(
height: width * 0.18,
width: MediaQuery.of(context).size.width / _iconList.length,
decoration: index == _selectedIndex
? BoxDecoration(
color: GlobalUniversal.blackForIcons.withOpacity(0.08),
border: Border(
bottom: BorderSide(width: 4, color: GlobalUniversal.purple),
),
/*
gradient: LinearGradient(colors: [
Colors.green.withOpacity(0.3),
Colors.green.withOpacity(0.015),
], begin: Alignment.bottomCenter, end: Alignment.topCenter)
*/
// color: index == _selectedItemIndex ? Colors.green : Colors.white,
)
: BoxDecoration(),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ImageIcon(
AssetImage(icon),
color: index == _selectedIndex
? GlobalUniversal.blackForIcons
: Colors.grey,
),
SizedBox(
height: 3.0,
),
Text(
text,
textScaleFactor: 1.0,
style: TextStyle(
color: index == _selectedIndex
? GlobalUniversal.blackForIcons
: Colors.grey,
fontFamily: lang != LANG_EN ? FONT_AR : FONT_EN),
),
],
),
),
Visibility(
visible: n != 0 && n != null,
child: Container(
margin: EdgeInsets.all(5.0),
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xffc32c37),
),
child: Text(
n.toString(),
textScaleFactor: 1.0,
style: TextStyle(
color: GlobalUniversal.whiteBG,
fontSize: 10.0),
),
),
),
],
),
);
}
}

The state is not updating because the number of the request value is not in a StreamBuilder which means it is not asynchronous.

Related

I tried to fetch data from firestore to chip widgets but then show "LateInitializationError". How I solve it?

I tried to fetch data from firestore to chip widgets but then show "LateInitializationError". And also chips should be can multi selection(select many chips). And also how to align 4 chips in a row like this example?I my code I think chips are show like ListView.
error..
I mean like this..
my code..
class uitry extends StatefulWidget {
const uitry({Key? key}) : super(key: key);
#override
State<uitry> createState() => _uitryState();
}
#override
Future<List<Words12>> fetchRecords() async {
var records = await FirebaseFirestore.instance.collection('12words').get();
return mapRecords(records);
}
List<Words12> mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var _list = records.docs
.map(
(words12) => Words12(
id: words12.id,
wordName: words12['wordName'],
categoryName: words12['categoryName'],
),
)
.toList();
return _list;
}
late int defaultChoiceIndex;
#override
void initState() {
initState();
defaultChoiceIndex = 0;
}
child: SizedBox(
width: width * 0.94,
height: height * 0.30,
child: FutureBuilder<List<Words12>>(
future: fetchRecords(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Words12> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return (ChoiceChip(
label: Text(data[index].wordName),
selected: defaultChoiceIndex == index,
selectedColor: Colors.deepPurple,
onSelected: (value) {
setState(() {
defaultChoiceIndex =
value ? index : defaultChoiceIndex;
});
},
// backgroundColor: color,
elevation: 1,
padding: const EdgeInsets.symmetric(
horizontal: 5.0),
));
},
);
}
}),
),
#override
void initState() {
initState();
defaultChoiceIndex = 0;
}
Should be:
#override
void initState() {
super.initState();
defaultChoiceIndex = 0;
}
I believe it will initialize your defaultChoiceIndex then.
For the alignment of chips: Wrap your ChoiceChip in the ListView.builder in a Row(), with a mainAxisAlignment of your choosing:
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ChoiceChip(etc.),
],
),
I tried it .Now it working..
code
SizedBox(
width: width * 0.94,
height: height * 0.30,
child: Column(
children: <Widget>[
const SizedBox(height: 16),
Wrap(
children: hobbyList.map(
(hobby) {
bool isSelected = false;
if (selectedHobby!.contains(hobby)) {
isSelected = true;
}
return GestureDetector(
onTap: () {
if (!selectedHobby!.contains(hobby)) {
if (selectedHobby!.length < 50) {
selectedHobby!.add(hobby);
setState(() {});
print(selectedHobby);
}
} else {
selectedHobby!.removeWhere(
(element) => element == hobby);
setState(() {});
print(selectedHobby);
}
},
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 5, vertical: 4),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 5, horizontal: 12),
decoration: BoxDecoration(
color: isSelected
? HexColor('#F5F185')
: HexColor('#D9D9D9'),
borderRadius:
BorderRadius.circular(18),
border: Border.all(
color: isSelected
? HexColor('#F5F185')
: HexColor('#D9D9D9'),
width: 2)),
child: Text(
hobby,
style: TextStyle(
color: isSelected
? Colors.black
: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600),
),
),
),
);
},
).toList(),
),
],
),
),
class _uitryState extends State<uitry> {
List<String> hobbyList = [
'Shopping',
'Brunch',
'Music',
'Road Trips',
'Tea',
'Trivia',
'Comedy',
'Clubbing',
'Drinking',
'Wine',
];
List<String>? selectedHobby = [];

Translate chat message based on language that user picks from drop down list

I am trying to get messages translated in real-time in the chat portion of my app depending on the language that the user picks in real-time. For example, if the user only speaks Spanish but messages from the user that they are chatting with are in English, then the user can select 'Spanish' from the dropdown list and all messages that have already been received and all future messages that they will receive will be translated into Spanish. I am capturing the sent message and its translation in each language in firebase but not sure how to get the messages to actually translate on the frontend. Any help would be much appreciated. Thank you in advance!
chat.dart
class Chat extends StatelessWidget {
final String? peerId;
final String? peerAvatar;
final String? name;
Chat({Key? key, this.peerId, this.peerAvatar, this.name}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: SkapeColors.pageBackgroundFifth,
resizeToAvoidBottomInset: true,
appBar: AppBar(
iconTheme: IconThemeData(
color: Colors.white,
),
backgroundColor: SkapeColors.pageBackground,
title: Text(
name!,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: 'Brand-Bold', fontWeight: FontWeight.bold, color: Colors.white, fontSize: 25),
),
centerTitle: true,
),
body: ChatScreen(
peerId: peerId,
peerAvatar: peerAvatar,
name: name,
),
);
}
}
class ChatScreen extends StatefulWidget {
final String? peerId;
final String? peerAvatar;
final String? name;
ChatScreen({Key? key, this.peerId, this.peerAvatar, this.name})
: super(key: key);
#override
State createState() =>
ChatScreenState(peerId: peerId, peerAvatar: peerAvatar);
}
class ChatScreenState extends State<ChatScreen> {
ChatScreenState({Key? key, this.peerId, this.peerAvatar, this.name});
String? peerId;
String? peerAvatar;
String? name;
String? id;
String? language1 = Translations.languages.first;
String? language2 = Translations.languages.first;
final translator = GoogleTranslator();
static final _apiKey = 'hidden';
List<QueryDocumentSnapshot> listMessage = new List.from([]);
int _limit = 20;
int _limitIncrement = 20;
String groupChatId = "";
SharedPreferences? prefs;
File? imageFile;
bool isLoading = false;
bool isShowSticker = false;
String imageUrl = "";
final TextEditingController textEditingController = TextEditingController();
final ScrollController listScrollController = ScrollController();
final FocusNode focusNode = FocusNode();
_scrollListener() {
if (listScrollController.offset >=
listScrollController.position.maxScrollExtent &&
!listScrollController.position.outOfRange) {
setState(() {
_limit += _limitIncrement;
});
}
}
#override
void initState() {
super.initState();
focusNode.addListener(onFocusChange);
listScrollController.addListener(_scrollListener);
readLocal();
}
readLocal() async {
prefs = await SharedPreferences.getInstance();
id = await getUserID();
if (id.hashCode <= peerId.hashCode) {
groupChatId = '$id-$peerId';
} else {
groupChatId = '$peerId-$id';
}
FirebaseFirestore.instance
.collection('users')
.doc(id)
.update({'chattingWith': peerId});
setState(() {});
}
static Future<String> translate(String message, String toLanguageCode) async {
final response = await http.post(
Uri.parse('https://translation.googleapis.com/language/translate/v2?target=$toLanguageCode&key=$_apiKey&q=$message'),
);
if (response.statusCode == 200) {
final body = json.decode(response.body);
final translations = body['data']['translations'] as List;
final translation = translations.first;
return HtmlUnescape().convert(translation['translatedText']);
} else {
throw Exception();
}
}
static Future<String> translate2(
String message, String fromLanguageCode, String toLanguageCode) async {
final translation = await GoogleTranslator().translate(
message,
from: fromLanguageCode,
to: toLanguageCode,
);
return translation.text;
}
Future<void> onSendMessage(String content, int type) async {
// type: 0 = text, 1 = image, 2 = sticker
if (content.trim() != '') {
textEditingController.clear();
var documentReference = FirebaseFirestore.instance
.collection('messages')
.doc(groupChatId)
.collection(groupChatId)
.doc(DateTime.now().millisecondsSinceEpoch.toString());
FirebaseFirestore.instance.runTransaction((transaction) async {
transaction.set(
documentReference,
{
'idFrom': id,
'idTo': peerId,
'timestamp': DateTime.now().millisecondsSinceEpoch.toString(),
'content': content,
'translated': {
'english': await translate(content, 'en'),
'spanish': await translate(content, 'es'),
'german': await translate(content, 'de'),
'french': await translate(content, 'fr'),
'russian': await translate(content, 'ru'),
'italian': await translate(content, 'it'),
'selectedTranslation': language1,
},
'type': type
},
);
});
listScrollController.animateTo(0.0,
duration: Duration(milliseconds: 300), curve: Curves.easeOut);
try {
String body = content;
if (content.contains("firebasestorage")) {
body = "Image";
}
var tempResp = await getUserInformation();
await sendNotificationToUser(
peerId, "New message from " + tempResp["fullName"], body);
} catch (e) {
print(e);
}
} else {
Fluttertoast.showToast(
msg: 'Nothing to send. Please insert your message',
backgroundColor: Colors.white24,
textColor: SkapeColors.colorPrimary);
}
}
Widget buildItem(int index, DocumentSnapshot document) {
String language1 = Translations.languages.first;
String language2 = Translations.languages.first;
if (document != null) {
if (document.get('idFrom') == id) {
// Right (my message)
return Row(
children: <Widget>[
document.get('type') == 0
// Text
? Container(
child: TranslationWidget(
message: document.get('content'),
fromLanguage: language1,
toLanguage: language1,
builder: (translatedMessage)=> MessageWidget(message: document.get('content'), translatedMessage: document.get('content'), isMe: true),
),
padding: EdgeInsets.fromLTRB(5.0, 5.0, 5.0, 5.0),
width: 215.0,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(8.0)),
margin: EdgeInsets.only(
bottom: isLastMessageRight(index) ? 20.0 : 10.0,
right: 10.0),
)
: document.get('type') == 1
// Image
? Container(
child: OutlinedButton(
child: Material(
child: Image.network(
document.get("content"),
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
),
width: 200.0,
height: 200.0,
child: Center(
child: CircularProgressIndicator(
color: SkapeColors.colorPrimary,
value: loadingProgress
.expectedTotalBytes !=
null &&
loadingProgress
.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
width: 200.0,
height: 200.0,
fit: BoxFit.cover,
),
borderRadius:
BorderRadius.all(Radius.circular(8.0)),
clipBehavior: Clip.hardEdge,
),
onPressed: () {
print("here");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FullPhoto(
url: document.get('content'),
),
),
);
},
// Sticker
: Container(
child: Image.asset(
'images/${document.get('content')}.gif',
width: 100.0,
height: 100.0,
fit: BoxFit.cover,
),
margin: EdgeInsets.only(
bottom: isLastMessageRight(index) ? 20.0 : 10.0,
right: 10.0),
),
],
mainAxisAlignment: MainAxisAlignment.end,
);
} else {
// Left (peer message)
return Container(
child: Column(
children: <Widget>[
Row(
children: <Widget>[
isLastMessageLeft(index)
? Material(
child: Image.network(
peerAvatar!,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
color: SkapeColors.colorPrimary,
value: loadingProgress.expectedTotalBytes !=
null &&
loadingProgress.expectedTotalBytes !=
null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
errorBuilder: (context, object, stackTrace) {
return Image.asset(
'images/user_icon.png',
height: 50,
width: 50,
);
},
width: 40,
height: 40,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.all(
Radius.circular(35.0),
),
clipBehavior: Clip.hardEdge,
)
: Container(width: 35.0),
document.get('type') == 0
? Container(
child:
TranslationWidget(
message: document.get('content'),
fromLanguage: language1,
toLanguage: language1,
builder: (translatedMessage)=> MessageWidget(message: document.get('content'), translatedMessage: document.get('content'), isMe: false),
),
padding: EdgeInsets.fromLTRB(10.0, 5.0, 5.0, 5.0),
width: 215.0,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(8.0)),
margin: EdgeInsets.only(
bottom: isLastMessageRight(index) ? 20.0 : 10.0,
right: 10.0),
).paddingOnly(left: 12)
: document.get('type') == 1
? Container(
child: TextButton(
child: Material(
child: Image.network(
document.get('content'),
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
),
width: 200.0,
height: 200.0,
child: Center(
child: CircularProgressIndicator(
color: SkapeColors.colorPrimary,
value: loadingProgress
.expectedTotalBytes !=
null &&
loadingProgress
.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
width: 200.0,
height: 200.0,
fit: BoxFit.cover,
),
borderRadius:
BorderRadius.all(Radius.circular(8.0)),
clipBehavior: Clip.hardEdge,
),
onPressed: () {
},
)
: Container(
),
],
),
)
],
crossAxisAlignment: CrossAxisAlignment.start,
),
margin: EdgeInsets.only(bottom: 2.0),
);
}
} else {
return SizedBox.shrink();
}
}
bool isLastMessageLeft(int index) {
if ((index > 0 && listMessage[index - 1].get('idFrom') == id) ||
index == 0) {
return true;
} else {
return false;
}
}
bool isLastMessageRight(int index) {
if ((index > 0 && listMessage[index - 1].get('idFrom') != id) ||
index == 0) {
return true;
} else {
return false;
}
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: ()=> Future.value(true),
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
// List of messages
buildListMessage(),
// Input content
buildInput(),
],
),
// Loading
buildLoading()
],
),
// onWillPop: onBackPress,
);
}
Widget buildLoading() {
return Positioned(
child: isLoading ? const Loading() : Container(),
);
}
Widget buildInput() {
return Container(
child: SingleChildScrollView(
child: Column(
children: [
buildTitle().paddingBottom(10),
Row(
),
SizedBox(width: 10,),
Flexible(
child: Container(
child: TextField(
cursorColor: SkapeColors.colorPrimary,
autocorrect: true,
onSubmitted: (value) {
onSendMessage(textEditingController.text, 0);
},
style: TextStyle(color: Colors.white, fontSize: 18.0),
controller: textEditingController,
decoration: InputDecoration.collapsed(
hintText: 'Send Message....',
hintStyle: TextStyle(color: SkapeColors.colorTextSemiLight),
),
focusNode: focusNode,
),
),
),
// Button send message
color: SkapeColors.pageBackground,
),
],
),
],
),
),
width: double.infinity,
height: 155.0,
// height: 100,
);
}
Widget buildListMessage() {
return Flexible(
child: groupChatId.isNotEmpty
? StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('messages')
.doc(groupChatId)
.collection(groupChatId)
.orderBy('timestamp', descending: true)
.limit(_limit)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
listMessage.addAll(snapshot.data!.docs);
return ListView.builder(
padding: EdgeInsets.all(10.0),
itemBuilder: (context, index) => buildItem(index, snapshot.data!.docs[index]),
itemCount: snapshot.data?.docs.length,
reverse: true,
controller: listScrollController,
);
} else {
return Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(SkapeColors.colorPrimary),
),
);
}
},
)
: Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(SkapeColors.colorPrimary),
),
),
);
}
Widget buildTitle() => TitleWidget(
language1: language1,
onChangedLanguage1: (newLanguage) => setState(() {
language1 = newLanguage;
}), key: ValueKey(DropDownWidget),
);
}
TitleWidget.dart
import 'package:flutter/material.dart';
import '../screens/messaging/chatWidgets/DropDownWidget.dart';
class TitleWidget extends StatelessWidget {
final String? language1;
final ValueChanged<String?> onChangedLanguage1;
const TitleWidget({
required this.language1,
required this.onChangedLanguage1,
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.translate, color: Colors.grey, size: 30,),
SizedBox(width: 12,),
DropDownWidget(
value: language1??"",
onChangedLanguage: onChangedLanguage1, key: key!,
),
],
);
}
translations.dart
class Translations {
static final languages = <String>[
'English',
'Spanish',
'French',
'German',
'Italian',
'Russian'
];
static String getLanguageCode(String language) {
switch (language) {
case 'English':
return 'en';
case 'French':
return 'fr';
case 'Italian':
return 'it';
case 'Russian':
return 'ru';
case 'Spanish':
return 'es';
case 'German':
return 'de';
default:
return 'en';
}
}
}
MessageWidget.dart
import 'package:flutter/material.dart';
class MessageWidget extends StatelessWidget {
final String? message;
final String? translatedMessage;
final bool isMe;
const MessageWidget({
required this.message,
required this.translatedMessage,
required this.isMe,
});
#override
Widget build(BuildContext context) {
final radius = Radius.circular(4);
final borderRadius = BorderRadius.all(radius);
return Row(
//To align at different positions based on if message is from the user or not
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
// padding: EdgeInsets.only(right: 10),
// margin: EdgeInsets.only(right: 10),
constraints: BoxConstraints(maxWidth: 190),
decoration: BoxDecoration(
color: isMe ? Theme.of(context).colorScheme.secondary : Colors.grey,
borderRadius: isMe
? borderRadius.subtract(BorderRadius.only(bottomRight: radius))
: borderRadius.subtract(BorderRadius.only(bottomLeft: radius)),
),
child: buildMessage(),
),
],
);
}
Widget buildMessage() => Column(
crossAxisAlignment:
isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
// Text(
// message,
// style: TextStyle(
// color: isMe ? Colors.black54 : Colors.white70,
// fontSize: 14,
// ),
// textAlign: isMe ? TextAlign.end : TextAlign.start,
// ),
Text(
translatedMessage!,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.start,
),
],
);
}
TranslationWidget.dart
class TranslationWidget extends StatefulWidget {
final String? message;
final String? fromLanguage;
final String? toLanguage;
final Widget Function(String? translation) builder;
const TranslationWidget({
required this.message,
required this.fromLanguage,
required this.toLanguage,
required this.builder,
Key? key,
}) : super(key: key);
#override
_TranslationWidgetState createState() => _TranslationWidgetState();
}
class _TranslationWidgetState extends State<TranslationWidget> {
String? translation;
#override
Widget build(BuildContext context) {
// final fromLanguageCode = Translations.getLanguageCode(widget.fromLanguage);
final toLanguageCode = Translations.getLanguageCode(widget.toLanguage!);
return FutureBuilder(
future: TranslationApi.translate(widget.message!, toLanguageCode),
//future: TranslationApi.translate2(
// widget.message, fromLanguageCode, toLanguageCode),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return buildWaiting();
default:
if (snapshot.hasError) {
translation = 'Could not translate due to Network problems';
} else {
translation = snapshot.data!;
}
return widget.builder(translation??"");
}
},
);
}
Widget buildWaiting() =>
translation == null ? Container() : widget.builder(translation??"");
}

Listview.Builder displays the the widget more than one time

I have two questions I hope you can help me with. I am building a listview.builder to loop through my array of objects and when I loop and display it displays the widget more than one time(photo is provided)
I am trying to iterate through my prefs of products to get the price of the product and quantity and add them to display the overall price, I tried forEach method but I couldn't quite figure out how.
Thanks for you help
class CheckOutCart extends StatefulWidget {
const CheckOutCart({Key? key}) : super(key: key);
#override
State<CheckOutCart> createState() => _CheckOutCartState();
}
class _CheckOutCartState extends State<CheckOutCart> {
late SharedPreferences sharedPrefs;
List<String>? cart;
// List<List<String ,int>> productsWithQuantity;
List productsWithQuantity = [];
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getPrefs(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// return Text(productsWithQuantity[0][1]);
return buildContainer(productsWithQuantity);
}
return Center(
child: CircularProgressIndicator()); // or some other widget
},
);
}
Future<void> _getPrefs() async {
sharedPrefs = await SharedPreferences.getInstance();
cart = sharedPrefs.getStringList('userCart');
getProductsAsObj(cart);
}
Container buildContainer(productsWithQuantity) {
final Random random = new Random(5);
return Container(
padding: EdgeInsets.symmetric(
vertical: getProportionateScreenWidth(15),
horizontal: getProportionateScreenWidth(30),
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30), topRight: Radius.circular(30)),
boxShadow: [
BoxShadow(
offset: Offset(0, -15),
blurRadius: 20,
color: Color(0XFFDADADA).withOpacity(0.15),
),
],
),
child: SafeArea(
child: ListView.builder(
shrinkWrap: true,
itemCount: productsWithQuantity.length, //length of cart
itemBuilder: (context, index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TotalPriceField(
titleText: "Merchandise Subtotal:\n",
priceText: productsWithQuantity[index][0].retail_price.toString(),
// priceText: productsWithQuantity.forEach((cart) => cart += cart[index][0].retail_price).toString()
// "\$375.5",
),
TotalPriceField(
titleText: "Shipping Total:\n",
priceText: "${random.nextInt(5)}",
),
],
),
SizedBox(
height: getProportionateScreenHeight(20),
),
TotalPriceField(
titleText: "Total Payment:\n",
priceText: "380.5",
),
SizedBox(
height: getProportionateScreenHeight(15),
),
SizedBox(
width: getProportionateScreenWidth(290),
child: DefaultButton(
text: "Check Out",
press: () {},
),
),
],
),
),
),
);
}
void getProductsAsObj(cart) {
for (var i = 0; i < cart.length; i++) {
var item = cart[i];
var items = item.split('-quantity-');
var product_ = items[0];
var quantity_ = items[1];
print(quantity_);
// product_ = '['+product_+']';
Map<String, dynamic> valueMap = json.decode(product_);
var product_obj = Product.fromMap(valueMap);
var itemx = [product_obj, quantity_];
productsWithQuantity.add(itemx);
}
}
}
```[![Listview.builder][1]][1]
[1]: https://i.stack.imgur.com/4wOyi.png

how to show only date number and name in CalendarTimeline() package?

I want to develope an application that help elderly like remind them medication time, and i want to add custom calendar in home screen that show the day number and name.
I have downloaded a package from internet called CalendarTimeline()
but it shows the calendar like this:
enter image description here
how can I show the custom calendar like this:
enter image description here
My code:
#override
Widget build(BuildContext context) {
return CalendarTimeline(
initialDate: DateTime(2020, 4, 20),
firstDate: DateTime(2019, 1, 15),
lastDate: DateTime(2020, 11, 20),
onDateSelected: (date) => print(date),
showYears: false,
leftMargin: 20,
monthColor: Colors.blueGrey,
dayColor: nave,
activeDayColor: nave,
activeBackgroundDayColor: Colors.yellow[100],
dotsColor: Color(0xFF333A47),
selectableDayPredicate: (date) => date.day != 23,
locale: 'en_ISO',
);
}
CalendarTimeline() class code:
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
typedef OnDateSelected = void Function(DateTime);
/// Creates a minimal, small profile calendar to select specific dates.
/// [initialDate] must not be [null], the same or after [firstDate] and
/// the same or before [lastDate]. [firstDate] must not be [null].
/// [lastDate] must not be null and the same or after [firstDate]
class CalendarTimeline extends StatefulWidget {
final DateTime initialDate;
final DateTime firstDate;
final DateTime lastDate;
final SelectableDayPredicate selectableDayPredicate;
final OnDateSelected onDateSelected;
final double leftMargin;
final Color dayColor;
final Color activeDayColor;
final Color activeBackgroundDayColor;
final Color monthColor;
final Color dotsColor;
final Color dayNameColor;
final String locale;
/// If true, it will show a separate row for the years.
/// It defaults to false
final bool showYears;
CalendarTimeline({
Key key,
#required this.initialDate,
#required this.firstDate,
#required this.lastDate,
#required this.onDateSelected,
this.selectableDayPredicate,
this.leftMargin = 0,
this.dayColor,
this.activeDayColor,
this.activeBackgroundDayColor,
this.monthColor,
this.dotsColor,
this.dayNameColor,
this.locale,
this.showYears = false,
}) : assert(initialDate != null),
assert(firstDate != null),
assert(lastDate != null),
assert(
initialDate.difference(firstDate).inDays >= 0,
'initialDate must be on or after firstDate',
),
assert(
!initialDate.isAfter(lastDate),
'initialDate must be on or before lastDate',
),
assert(
!firstDate.isAfter(lastDate),
'lastDate must be on or after firstDate',
),
assert(
selectableDayPredicate == null || selectableDayPredicate(initialDate),
'Provided initialDate must satisfy provided selectableDayPredicate',
),
assert(
locale == null || dateTimeSymbolMap().containsKey(locale),
'Provided locale value doesn\'t exist',
),
super(key: key);
#override
_CalendarTimelineState createState() => _CalendarTimelineState();
}
class _CalendarTimelineState extends State<CalendarTimeline> {
final ItemScrollController _controllerYear = ItemScrollController();
final ItemScrollController _controllerMonth = ItemScrollController();
final ItemScrollController _controllerDay = ItemScrollController();
int _yearSelectedIndex;
int _monthSelectedIndex;
int _daySelectedIndex;
double _scrollAlignment;
List<DateTime> _years = [];
List<DateTime> _months = [];
List<DateTime> _days = [];
DateTime _selectedDate;
String get _locale =>
widget.locale ?? Localizations.localeOf(context).languageCode;
/// Populates the calendar and animates to the [widget.initialDate]
#override
void initState() {
super.initState();
_initCalendar();
_scrollAlignment = widget.leftMargin / 440;
SchedulerBinding.instance.addPostFrameCallback((_) {
initializeDateFormatting(_locale);
});
}
/// Refreshes the calendar when a day, month or year is selected
#override
void didUpdateWidget(CalendarTimeline oldWidget) {
super.didUpdateWidget(oldWidget);
_initCalendar();
if (widget.showYears) _moveToYearIndex(_yearSelectedIndex);
_moveToMonthIndex(_monthSelectedIndex);
_moveToDayIndex(_daySelectedIndex);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (widget.showYears) _buildYearList(),
_buildMonthList(),
_buildDayList(),
],
);
}
/// Creates the row with the day of the [selectedDate.month]. If the
/// [selectedDate.year] && [selectedDate.month] is the [widget.firstDate] or [widget.lastDate]
/// the days show will be de availables
SizedBox _buildDayList() {
return SizedBox(
height: 75,
child: ScrollablePositionedList.builder(
itemScrollController: _controllerDay,
initialScrollIndex: _daySelectedIndex,
initialAlignment: _scrollAlignment,
scrollDirection: Axis.horizontal,
itemCount: _days.length,
padding: EdgeInsets.only(left: widget.leftMargin, right: 10),
itemBuilder: (BuildContext context, int index) {
final currentDay = _days[index];
final shortName =
DateFormat.E(_locale).format(currentDay).capitalize();
return Row(
children: <Widget>[
_DayItem(
isSelected: _daySelectedIndex == index,
dayNumber: currentDay.day,
shortName: shortName.length > 3
? shortName.substring(0, 3)
: shortName,
onTap: () => _goToActualDay(index),
available: widget.selectableDayPredicate == null
? true
: widget.selectableDayPredicate(currentDay),
dayColor: widget.dayColor,
activeDayColor: widget.activeDayColor,
activeDayBackgroundColor: widget.activeBackgroundDayColor,
dotsColor: widget.dotsColor,
dayNameColor: widget.dayNameColor,
),
if (index == _days.length - 1)
SizedBox(
width: MediaQuery.of(context).size.width -
widget.leftMargin -
65)
],
);
},
),
);
}
/// Creates the row with all the months in the calendar. If [widget.showYears] is set to true
/// it will only show the months allowed in the selected year. By default it will show all
/// months in the calendar and the small version of [YearName] for each year in between
Widget _buildMonthList() {
return Container(
height: 40,
child: ScrollablePositionedList.builder(
initialScrollIndex: _monthSelectedIndex,
initialAlignment: _scrollAlignment,
itemScrollController: _controllerMonth,
padding: EdgeInsets.only(left: widget.leftMargin),
scrollDirection: Axis.horizontal,
itemCount: _months.length,
itemBuilder: (BuildContext context, int index) {
final currentDate = _months[index];
final monthName = DateFormat.MMMM(_locale).format(currentDate);
return Padding(
padding: const EdgeInsets.only(right: 12.0, left: 4.0),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
if (widget.firstDate.year != currentDate.year &&
currentDate.month == 1 &&
!widget.showYears)
Padding(
padding: const EdgeInsets.only(right: 10),
child: YearName(
name: DateFormat.y(_locale).format(currentDate),
color: widget.monthColor,
),
),
MonthName(
isSelected: _monthSelectedIndex == index,
name: monthName,
onTap: () => _goToActualMonth(index),
color: widget.monthColor,
),
if (index == _months.length - 1)
SizedBox(
width: MediaQuery.of(context).size.width -
widget.leftMargin -
(monthName.length * 10),
)
],
),
);
},
),
);
}
/// Creates the row with all the years in the calendar. It will only show if
/// [widget.showYears] is set to true. It is false by default
Widget _buildYearList() {
return Container(
height: 40,
child: ScrollablePositionedList.builder(
initialScrollIndex: _yearSelectedIndex,
initialAlignment: _scrollAlignment,
itemScrollController: _controllerYear,
padding: EdgeInsets.only(left: widget.leftMargin),
scrollDirection: Axis.horizontal,
itemCount: _years.length,
itemBuilder: (BuildContext context, int index) {
final currentDate = _years[index];
final yearName = DateFormat.y(_locale).format(currentDate);
return Padding(
padding: const EdgeInsets.only(right: 12.0, left: 4.0),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
YearName(
isSelected: _yearSelectedIndex == index,
name: yearName,
onTap: () => _goToActualYear(index),
color: widget.monthColor,
small: false,
),
if (index == _years.length - 1)
SizedBox(
width: MediaQuery.of(context).size.width -
widget.leftMargin -
(yearName.length * 10),
)
],
),
);
},
),
);
}
/// It will populate the [_days] list with all the allowed days. Adding all days of the month
/// when the [selectedDate.month] is not the first or the last in [widget.firstDate] or [widget.lastDate].
/// In that case it will only show the allowed days from and up to the specified in [widget.firstDate]
/// and [widget.lastDate]
_generateDays(DateTime selectedDate) {
_days.clear();
for (var i = 1; i <= 31; i++) {
final day = DateTime(selectedDate.year, selectedDate.month, i);
if (day.difference(widget.firstDate).inDays < 0) continue;
if (day.month != selectedDate.month || day.isAfter(widget.lastDate))
break;
_days.add(day);
}
}
/// It will populate the [_months] list. If [widget.showYears] is true, it will add from January
/// to December, unless the selected year is the [widget.firstDate.year] or the [widget.lastDate.year].
/// In that case it will only from and up to the allowed months in [widget.firstDate] and [widget.lastDate].
/// By default, when [widget.showYears] is false, it will add all months from [widget.firstDate] to
/// [widget.lastDate] and all in between
_generateMonths(DateTime selectedDate) {
_months.clear();
if (widget.showYears) {
int month = selectedDate.year == widget.firstDate.year
? widget.firstDate.month
: 1;
DateTime date = DateTime(selectedDate.year, month);
while (date.isBefore(DateTime(selectedDate.year + 1)) &&
date.isBefore(widget.lastDate)) {
_months.add(date);
date = DateTime(date.year, date.month + 1);
}
} else {
DateTime date = DateTime(widget.firstDate.year, widget.firstDate.month);
while (date.isBefore(widget.lastDate)) {
_months.add(date);
date = DateTime(date.year, date.month + 1);
}
}
}
/// It will populate the [_years] list with the years between firstDate and lastDate
_generateYears() {
_years.clear();
DateTime date = widget.firstDate;
while (date.isBefore(widget.lastDate)) {
_years.add(date);
date = DateTime(date.year + 1);
}
}
/// It will reset the calendar to the initial date
_resetCalendar(DateTime date) {
if (widget.showYears) _generateMonths(date);
_generateDays(date);
_daySelectedIndex = date.month == _selectedDate.month
? _days.indexOf(
_days.firstWhere((dayDate) => dayDate.day == _selectedDate.day))
: null;
_controllerDay.scrollTo(
index: _daySelectedIndex ?? 0,
alignment: _scrollAlignment,
duration: Duration(milliseconds: 500),
curve: Curves.easeIn,
);
}
_goToActualYear(int index) {
_moveToYearIndex(index);
_yearSelectedIndex = index;
_resetCalendar(_years[index]);
setState(() {});
}
void _moveToYearIndex(int index) {
_controllerYear.scrollTo(
index: index,
alignment: _scrollAlignment,
duration: Duration(milliseconds: 500),
curve: Curves.easeIn,
);
}
_goToActualMonth(int index) {
_moveToMonthIndex(index);
_monthSelectedIndex = index;
_resetCalendar(_months[index]);
setState(() {});
}
void _moveToMonthIndex(int index) {
_controllerMonth.scrollTo(
index: index,
alignment: _scrollAlignment,
duration: Duration(milliseconds: 500),
curve: Curves.easeIn,
);
}
_goToActualDay(int index) {
_moveToDayIndex(index);
_daySelectedIndex = index;
_selectedDate = _days[index];
widget.onDateSelected(_selectedDate);
setState(() {});
}
void _moveToDayIndex(int index) {
_controllerDay.scrollTo(
index: index,
alignment: _scrollAlignment,
duration: Duration(milliseconds: 500),
curve: Curves.easeIn,
);
}
selectedYear() {
_yearSelectedIndex = _years.indexOf(_years
.firstWhere((yearDate) => yearDate.year == widget.initialDate.year));
}
selectedMonth() {
if (widget.showYears)
_monthSelectedIndex = _months.indexOf(_months.firstWhere(
(monthDate) => monthDate.month == widget.initialDate.month));
else
_monthSelectedIndex = _months.indexOf(_months.firstWhere((monthDate) =>
monthDate.year == widget.initialDate.year &&
monthDate.month == widget.initialDate.month));
}
selectedDay() {
_daySelectedIndex = _days.indexOf(
_days.firstWhere((dayDate) => dayDate.day == widget.initialDate.day));
}
/// Initializes the calendar. It will be executed every time a new date is selected
_initCalendar() {
_selectedDate = widget.initialDate;
_generateMonths(_selectedDate);
_generateDays(_selectedDate);
if (widget.showYears) {
_generateYears();
selectedYear();
}
selectedMonth();
selectedDay();
}
}
/// Creates a Widget to represent the years. By default it will show the smaller version
/// in the months row. If [small] is set to false it will show the bigger version for the
/// years row. In the smaller version the [onTap] property is not available
class YearName extends StatelessWidget {
final String name;
final Function onTap;
final bool isSelected;
final Color color;
final bool small;
YearName(
{this.name,
this.onTap,
this.isSelected = false,
this.color,
this.small = true});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: small ? null : onTap,
child: Container(
decoration: isSelected || small
? BoxDecoration(
border: Border.all(color: color ?? Colors.black87, width: 1),
borderRadius: BorderRadius.circular(4),
)
: null,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14.0, vertical: 5.0),
child: Text(
name.toUpperCase(),
style: TextStyle(
fontSize: small ? 12 : 20,
color: color ?? Colors.black87,
fontWeight: FontWeight.bold,
),
),
),
),
);
}
}
/// Creates a Widget to represent the monts.
class MonthName extends StatelessWidget {
final String name;
final Function onTap;
final bool isSelected;
final Color color;
MonthName({this.name, this.onTap, this.isSelected, this.color});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: this.onTap,
child: Text(
this.name.toUpperCase(),
style: TextStyle(
fontSize: 14,
color: color ?? Colors.black87,
fontWeight: this.isSelected ? FontWeight.bold : FontWeight.w300,
),
),
);
}
}
/// Creates a Widget representing the day.
class _DayItem extends StatelessWidget {
final int dayNumber;
final String shortName;
final bool isSelected;
final Function onTap;
final Color dayColor;
final Color activeDayColor;
final Color activeDayBackgroundColor;
final bool available;
final Color dotsColor;
final Color dayNameColor;
const _DayItem({
Key key,
#required this.dayNumber,
#required this.shortName,
#required this.isSelected,
#required this.onTap,
this.dayColor,
this.activeDayColor,
this.activeDayBackgroundColor,
this.available = true,
this.dotsColor,
this.dayNameColor,
}) : super(key: key);
final double height = 70.0;
final double width = 60.0;
///? I united both widgets to increase the touch target of non selected days by using a transparent box decorator.
///? Now if the user click close to the number but not straight on top it will still select the date. (ONLY INFORMATION - ERASE)
_buildDay(BuildContext context) {
final textStyle = TextStyle(
color: available
? dayColor ?? Theme.of(context).accentColor
: dayColor?.withOpacity(0.5) ??
Theme.of(context).accentColor.withOpacity(0.5),
fontSize: 32,
fontWeight: FontWeight.normal);
final selectedStyle = TextStyle(
color: activeDayColor ?? Colors.white,
fontSize: 32,
fontWeight: FontWeight.bold,
height: 0.8,
);
return GestureDetector(
onTap: available ? onTap : null,
child: Container(
decoration: isSelected
? BoxDecoration(
color:
activeDayBackgroundColor ?? Theme.of(context).accentColor,
borderRadius: BorderRadius.circular(12.0),
)
: BoxDecoration(color: Colors.transparent),
height: height,
width: width,
child: Column(
children: <Widget>[
if (isSelected) ...[
SizedBox(height: 7),
_buildDots(),
SizedBox(height: 12),
] else
SizedBox(height: 14),
Text(
dayNumber.toString(),
style: isSelected ? selectedStyle : textStyle,
),
if (isSelected)
Text(
shortName,
style: TextStyle(
color: dayNameColor ?? activeDayColor ?? Colors.white,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
],
),
),
);
}
Widget _buildDots() {
final dot = Container(
height: 5,
width: 5,
decoration: new BoxDecoration(
color: this.dotsColor ?? this.activeDayColor ?? Colors.white,
shape: BoxShape.circle,
),
);
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [dot, dot],
);
}
#override
Widget build(BuildContext context) {
return _buildDay(context);
}
}
extension StringExtension on String {
String capitalize() {
if (this.isEmpty) {
return this;
}
return this[0].toUpperCase() + this.substring(1);
}
}
Is this more close to what you are looking for?
DatePicker(
DateTime.now(),
initialSelectedDate: DateTime.now(),
selectionColor: Colors.black,
selectedTextColor: Colors.white,
onDateChange: (date) {
// New date selected
setState(() {
_selectedValue = date;
});
},
),

How to show all dates in a horizontal scroll view

I want to have all the dates of a month in a horizontal scroll view. The current week 7 days should be displayed first and on scrolling right the previous dates should be shown. later on scrolling left the later weeks dates should be displayed an don tap of a date i should get the date in return. How to do this? I have tried using the below. It displays dates and scrolls horizontally as well but it displays only multiple of 7 and all the exact dates of a month. Also on tapping the date it does not return the position form listview builder as 0 and i returns the index.
Widget displaydates(int week) {
return ListView.builder(
itemCount: 5,
itemBuilder: (BuildContext context, int position) {
return Row(
children: <Widget>[
for (int i = 1; i < 8; i++)
Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
print(position);
},
child: Text(
((week * 7) + i).toString() + " ",
style: TextStyle(),
),
),
),
],
);
});
}
I am calling this like:
displaydates(0),
displaydates(1),
displaydates(2),
displaydates(3),
displaydates(4),
UPDATE:
You can get last day of month using below code :
DateTime(year,month + 1).subtract(Duration(days: 1)).day;
You should use ModalBottomSheet for the same and then pop that on selection with the Navigator.of(context).pop(result);
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return ListView.builder(
itemCount: 5,
itemBuilder: (BuildContext context, int position) {
return Row(
children: <Widget>[
for (int i = 1; i < 8; i++)
Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
Navigator.of(context).pop(position);
},
child: Text(
((week * 7) + i).toString() + " ",
style: TextStyle(),
),
),
),
],
);
}
);
}
);
I have created a widget that let me select date from a list and it is scrollable(along time ago). There lot of code but you can use the selected date under any widget which parent wrapped from InheritedWidget.
Here is the code(Note that I also created a package for this, if you dont like to write this much code for this):
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class MyInheritedWidget extends InheritedWidget {
final DateTime date;
final int selectedDay;
final int monthDateCount;
final bool isDateHolderActive;
final Map<int, bool> dayAvailabilityMap;
final ValueChanged<bool> toggleDateHolderActive;
final ValueChanged<int> setSelectedDay;
MyInheritedWidget({
Key key,
this.date,
this.selectedDay,
this.monthDateCount,
this.isDateHolderActive,
this.dayAvailabilityMap,
this.toggleDateHolderActive,
this.setSelectedDay,
Widget child,
}) : super(key: key, child: child);
#override
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return oldWidget.selectedDay != selectedDay ||
oldWidget.toggleDateHolderActive != toggleDateHolderActive;
}
}
class DateIndicatorPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
DateIndicator(),
Expanded(
child: Container(),
),
],
),
),
);
}
}
class DateIndicator extends StatefulWidget {
static MyInheritedWidget of(BuildContext context) => context.dependOnInheritedWidgetOfExactType();
#override
_DateIndicatorState createState() => _DateIndicatorState();
}
class _DateIndicatorState extends State<DateIndicator> {
DateTime date = DateTime.now();
int selectedDay = 1;
int monthDateCount = 1;
bool isDateHolderActive = false;
Map<int, bool> dayAvailabilityMap = {};
void toggleDateHolderActive(bool flag) {
setState(() {
isDateHolderActive = flag;
});
}
void setSelectedDay(int index) {
setState(() {
selectedDay = index;
});
}
#override
void initState() {
final DateTime dateForValues = new DateTime(date.year, date.month + 1, 0);
monthDateCount = dateForValues.day;
// Just to show how to activate when something exist for this day(from network response or something)
dayAvailabilityMap[1] = true;
dayAvailabilityMap[2] = true;
dayAvailabilityMap[3] = true;
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: 68.0,
padding:
const EdgeInsets.only(left: 7.0, right: 3.0, top: 2.0, bottom: 2.0),
decoration: BoxDecoration(
color: Theme.of(context).secondaryHeaderColor,
boxShadow: [
BoxShadow(
color: Colors.blueAccent.withOpacity(.7),
offset: Offset(0.0, .5),
blurRadius: 3.0,
spreadRadius: 0.3),
],
),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: monthDateCount, // to avoid showing zero
itemBuilder: (BuildContext context, int index) {
return MyInheritedWidget(
date: date,
selectedDay: selectedDay,
monthDateCount: monthDateCount,
isDateHolderActive: isDateHolderActive,
dayAvailabilityMap: dayAvailabilityMap,
toggleDateHolderActive: toggleDateHolderActive,
setSelectedDay: setSelectedDay,
child: DateHolder(index));
}),
);
}
}
class DateHolder extends StatelessWidget {
DateHolder(this.index);
final int index;
final Widget activeBubble = Container(
width: 15.0,
height: 15.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.deepOrangeAccent,
),
);
#override
Widget build(BuildContext context) {
final appState = DateIndicator.of(context);
return InkWell(
onTap: () {
appState.toggleDateHolderActive(true);
appState.setSelectedDay(index);
print("Date ${index} selected!");
},
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
Container(
margin: const EdgeInsets.only(right: 5.0),
child: Text(
"${DateFormat('EEEE').format(DateTime(appState.date.year, appState.date.month, index)).substring(0, 1)}",
style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 12.0),
)),
Container(
width: 45.0,
height: 45.0,
margin: const EdgeInsets.only(right: 5.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
border: (index == (appState.selectedDay) &&
appState.isDateHolderActive == true)
? Border.all(width: 2.0, color: Theme.of(context).primaryColor)
: Border.all(color: Colors.transparent),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Text(
"${index + 1}", // to avoid showing zero
style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 16.0),
),
),
),
),
],
),
(appState.dayAvailabilityMap[index] ?? false)
? Positioned(right: 8.0, bottom: 5.0, child: activeBubble)
: Container(),
],
),
);
}
}