Here is the image i am trying to build. Tried Stack with Alignment.topRight but i need to build a TextField after the IconButton that's the bottleneck.
Little details: The grey box is the input field, which i will append some static text at start, as you can see in the pic below. Static Text = Happy Birthday, then the button with cross icon and after that a TextField as shown in the design.
I tried different approaches but not coming-up with accurate results.
Row(
children: [
Container(
margin: const EdgeInsets.only(top: 5, left: 5),
padding: const EdgeInsets.only(top: 10, left: 10, bottom: 10),
decoration:
BoxDecoration(color: Constants.colorWhite.withOpacity(0.90)),
child: Text(Constants.happyBirthday),
),
Container(
margin: const EdgeInsets.only(top: 5, right: 0),
padding:
const EdgeInsets.only(left: 5, top: 10, right: 0, bottom: 10),
decoration:
BoxDecoration(color: Constants.colorWhite.withOpacity(0.50)),
child: Stack(
children: [
Text(widget.userName.toString()),
const Positioned(top: 0, right: 0, child: Icon(Icons.close))
],
)),
Container(
margin: const EdgeInsets.only(top: 5, left: 0),
padding: const EdgeInsets.only(top: 10, left: 10, bottom: 10),
decoration:
BoxDecoration(color: Constants.colorWhite.withOpacity(0.90)),
child: Text('${Constants.happyBirthday} '
'${Constants.happyBirthday}'
'${Constants.happyBirthday}'),
),
],
)
You can create your own TextEditingController and override buildTextSpan.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// #techbusiness
///
/// NOTE: Needs to be improved. Feel free to play with the styling
/// to get your desired result. This is just to show you the
/// possibility of your design.
class TaggerTextEditingController extends TextEditingController {
TaggerTextEditingController({
this.onPressTag,
this.onDeleteTag,
this.tagger = '#',
this.tagStyle,
this.tagBackgroundColor,
this.deleteIconSize = 15,
super.text,
});
final ValueChanged<String>? onPressTag;
final ValueChanged<String>? onDeleteTag;
final String tagger;
final TextStyle? tagStyle;
final Color? tagBackgroundColor;
final double deleteIconSize;
void _onDeleteTag(String tag) {
text = text.replaceFirst(tag, '');
onDeleteTag?.call(tag.replaceFirst(tagger, ''));
}
WidgetSpan _buildTag(String tag, TextStyle? style) => WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Stack(
clipBehavior: Clip.none,
children: <Widget>[
InkWell(
onTap: () => onPressTag?.call(tag.replaceFirst(tagger, '')),
borderRadius: const BorderRadius.all(Radius.circular(15)),
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(15)),
color: tagBackgroundColor ?? Colors.grey.shade200,
),
child: Text(
tag.replaceFirst(tagger, ''),
style: style,
),
),
),
Positioned(
top: -deleteIconSize / 4,
right: -deleteIconSize / 4,
child: GestureDetector(
onTap: () => _onDeleteTag(tag),
child: Icon(
CupertinoIcons.xmark_circle_fill,
size: deleteIconSize,
color: Colors.red,
),
),
),
],
),
);
#override
TextSpan buildTextSpan({
required BuildContext context,
TextStyle? style,
required bool withComposing,
}) {
final List<TaggerText> texts = TaggerText.getTags(text, tagger);
return TextSpan(
children: texts
.map(
(TaggerText value) => value.isTag
? _buildTag(value.text, tagStyle ?? style)
: TextSpan(
text: value.text,
style: style,
),
)
.toList(),
);
}
}
//------------------------------------------------------------------------------
class TaggerText {
const TaggerText(this.text, this.isTag);
final String text;
final bool isTag;
static List<TaggerText> getTags(String text, String tagger) {
final List<TaggerText> tags = <TaggerText>[];
StringBuffer textPortion = StringBuffer();
String prevChar = '';
bool isTextPortionATag() => textPortion.toString().startsWith(tagger);
void addTaggerText() => tags.add(
TaggerText(textPortion.toString(), isTextPortionATag()),
);
for (final String char in text.characters) {
if (char == tagger && prevChar == ' ') {
addTaggerText();
textPortion.clear();
} else if (char == ' ' && isTextPortionATag()) {
addTaggerText();
textPortion.clear();
}
textPortion.write(char);
prevChar = char;
}
if (textPortion.isNotEmpty) {
addTaggerText();
}
return tags;
}
}
How to use.
TextField(
controller: TaggerTextEditingController(),
maxLines: 5,
),
Related
import 'package:flutter/material.dart';
import '../main.dart';
import 'colors.dart';
import 'todo_item.dart';
import 'todo.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Toodoo extends StatefulWidget {
const Toodoo({Key? key}) : super(key: key);
#override
State<Toodoo> createState() => _ToodooState();
}
class _ToodooState extends State<Toodoo> {
final todosList = ToDo.todoList();
List<ToDo> _foundToDo = [];
final _todoController = TextEditingController();
final GlobalKey<ScaffoldState> _key = GlobalKey();
String ""
#override
void initState() {
_foundToDo = todosList;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.menu, color: Colors.black),
onPressed: () => _key.currentState!.openDrawer(),
),
backgroundColor: const Color(0xff346594),
title: const Text("ToDos", style: TextStyle(color: Colors.black)),
),
backgroundColor: tdBGColor,
body: Stack(
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 15,
),
child: Column(
children: [
searchBox(),
Expanded(
child: ListView(
children: [
Container(
margin: const EdgeInsets.only(
top: 50,
bottom: 20,
),
child: const Text(
'All ToDos',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
for (ToDo todo in _foundToDo.reversed)
ToDoItem(
todo: todo,
onToDoChanged: _handleToDoChange,
onDeleteItem: _deleteToDoItem,
),
],
),
)
],
),
),
Align(
alignment: Alignment.bottomCenter,
child: Row(children: [
Expanded(
child: Container(
margin: const EdgeInsets.only(
bottom: 20,
right: 20,
left: 20,
),
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 5,
),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: const [
BoxShadow(
color: Colors.grey,
offset: Offset(0.0, 0.0),
blurRadius: 10.0,
spreadRadius: 0.0,
),
],
borderRadius: BorderRadius.circular(10),
),
child: TextField(
controller: _todoController,
decoration: const InputDecoration(
hintText: 'Add a new todo item',
border: InputBorder.none),
),
),
),
Container(
margin: const EdgeInsets.only(
bottom: 20,
right: 20,
),
child: ElevatedButton(
onPressed: () {
_addToDoItem(_todoController.text);
},
style: ElevatedButton.styleFrom(
backgroundColor: tdBlue,
minimumSize: const Size(60, 60),
elevation: 10,
),
child: const Text('+', style: TextStyle(fontSize: 40),),
),
),
]),
),
],
),
drawer: const Navigation(),
);
}
void _handleToDoChange(ToDo todo) {
setState(() {
todo.isDone = !todo.isDone;
});
}
void _deleteToDoItem(String id) {
setState(() {
todosList.removeWhere((item) => item.id == id);
});
}
void _addToDoItem(String toDo) async{
final sp = await SharedPreferences.getInstance();
setState(() {
todosList.add(ToDo(
id: DateTime.now().millisecondsSinceEpoch.toString(),
todoText: toDo,
));
});
sp.setString(id, todo)
_todoController.clear();
}
void _runFilter(String enteredKeyword) {
List<ToDo> results = [];
if (enteredKeyword.isEmpty) {
results = todosList;
} else {
results = todosList
.where((item) => item.todoText!
.toLowerCase()
.contains(enteredKeyword.toLowerCase()))
.toList();
}
setState(() {
_foundToDo = results;
});
}
Widget searchBox() {
return Container(
);
}
}
I am trying to save todo data locally, using shared preferences but don't know how to implement this, any help on this will be appreciated.Shared preferences is the best thing to use in such apps, so that's why I am using shared preference instead of firebase.
I have initialized Shared preferences in future but the thing is how to read and show the data with the controller given above the code.
Use Hive database or sqflite to save such kind of data(Good practice).You should use shared preference to store small bunch of data.
Yeah, shared preference is a good way to store data permanently on the local device. I can suggest you one way of doing this.
You need to create only one key (and value), and the value would be a stringified array. Every time user created new todo, you first need to pull the previous array, parse it to JSON, push the latest todo in that array, and set the key value again.
This array will also help you if you want to show the user all the todos by pulling the data only from one key, cause all the todos will the store in one array.
var todos = [
{
"id": "",
"todoText": "''"
},
{
"id": "",
"todoText": "''"
},
...
]
But you need to store stringified array, so you need to parse back to JSON after get data from shared preferences
I wrote the code to get data from List to chips and when click chips the colour changed to blue. But I want to fetch data from firestore instead "words list".
Instead this words list ...
Database collection image
I want to display "WordName" field in the chips.
My code..
class uitry extends StatefulWidget {
const uitry({Key? key}) : super(key: key);
#override
State<uitry> createState() => _uitryState();
}
class _uitryState extends State<uitry> {
List<String> wordList = [
'Shopping',
'Brunch',
'Music',
'Road Trips',
'Tea',
'Trivia',
'Comedy',
'Clubbing',
'Drinking',
'Wine',
];
List<String> selectedWord = [];
List<String>? deSelectedWord = [];
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
return Scaffold(
body: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(Config.app_background4), fit: BoxFit.fill),
),
child: SafeArea(
child: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 14, right: 0),
child: Column(
children: [
SizedBox(
width: width * 0.94,
height: height * 0.30,
child: Column(
children: <Widget>[
const SizedBox(height: 16),
Wrap(
children: wordList.map(
(word) {
bool isSelected = false;
if (selectedWord!.contains(word)) {
isSelected = true;
}
return GestureDetector(
onTap: () {
if (!selectedWord!.contains(word)) {
if (selectedWord!.length < 50) {
selectedWord!.add(word);
deSelectedWord!.removeWhere(
(element) => element == word);
setState(() {});
print(selectedWord);
}
} else {
selectedWord!.removeWhere(
(element) => element == word);
deSelectedWord!.add(word);
setState(() {
// selectedHobby.remove(hobby);
});
print(selectedWord);
print(deSelectedWord);
}
},
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('#0000FF')
: HexColor('#D9D9D9'),
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: isSelected
? HexColor('#0000FF')
: HexColor('#D9D9D9'),
width: 2)),
child: Text(
word,
style: TextStyle(
color: isSelected
? Colors.black
: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600),
),
),
),
);
},
).toList(),
),
],
),
),
],
),
),
],
))),
),
);
}
}
How get that from firestore? I hope You can understand what I ask. Thank you!
I would do the following:
Initialize your list of words to an empty list
Use the initState method of the stateful widget to make a call to firestore to fetch all the documents that have the wordName property and get the word from the result and set it to a new list
Assign the new list to the wordList property and setState to re-render.
This would be it to get the words and set the chips with fetched words.
Keep in mind that since you are making an async call to firestore you should show some form of loading to tell the user you are fetching the data otherwise you would show and empty chip list until you fetch the data.
I created a stepper (4 steps) with two buttons for next and previous. Each step has a form, and each form is in a widget in its own class.
The first problem is that every time I click the previous button, the data in the text fields disappear.
How can I preserve the state of each widget in each step?
The second problem, I want the last step to be a summary of what the user has entered in the previous steps. What is the best way to get the data from each step and display them in the last step?
I would really appreciate it if you could give me a solution. Thank you
I tried using AutomaticKeepAliveClientMixin but it didn't work .
import 'package:flutter/material.dart';
class CustomeStepper extends StatelessWidget {
final double width;
final List<IconData> icons;
final List<String> titles;
final int curStep;
final Color circleActiveColor;
final Color circleInactiveColor;
final Color iconActiveColor;
final Color iconInactiveColor;
final Color textActiveColor;
final Color textInactiveColor;
final double lineWidth = 4.0;
final List<Widget> content;
CustomeStepper(
{required this.icons,
required this.curStep,
required this.titles,
required this.width,
required this.circleActiveColor,
required this.circleInactiveColor,
required this.iconActiveColor,
required this.iconInactiveColor,
required this.textActiveColor,
required this.textInactiveColor,
required this.content})
: assert(curStep > 0 && curStep <= icons.length),
assert(width > 0);
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Container(
width: width,
padding: const EdgeInsets.only(
top: 32.0,
left: 24.0,
right: 24.0,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
children: _iconViews(),
),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _titleViews(),
),
Expanded(
child: Container(
margin: const EdgeInsets.only(top: 16),
child: content[curStep - 1]),
)
],
)),
);
}
List<Widget> _iconViews() {
var list = <Widget>[];
icons.asMap().forEach((i, icon) {
var circleColor = (i == 0 || curStep >= i + 1)
? circleActiveColor
: circleInactiveColor;
var lineColor = (i == 0 || curStep >= i + 1)
? circleActiveColor
: circleInactiveColor;
var iconColor =
(i == 0 || curStep >= i + 1) ? iconActiveColor : iconInactiveColor;
list.add(
Container(
width: 50.0,
height: 50.0,
padding: const EdgeInsets.all(0),
child: Icon(
icon,
color: iconColor,
size: 25.0,
),
decoration: BoxDecoration(
color: circleColor,
borderRadius: const BorderRadius.all(
Radius.circular(25.0),
),
),
),
);
//line between icons
if (i != icons.length - 1) {
list.add(Expanded(
child: Container(
height: lineWidth,
color: lineColor,
)));
}
});
return list;
}
List<Widget> _titleViews() {
var list = <Widget>[];
titles.asMap().forEach((i, text) {
var _textColor =
(i == 0 || curStep > i + 1) ? textActiveColor : textInactiveColor;
list.add(
Container(
width: 50.0,
alignment: Alignment.topCenter,
padding: const EdgeInsets.all(0),
child: Text(
text,
textAlign: TextAlign.center,
style: TextStyle(color: _textColor, fontWeight: FontWeight.bold),
),
),
);
});
return list;
}
}
import 'package:flutter/material.dart';
import 'package:project_five/widgets/business/adding_product_widgets/first_step.dart';
import 'package:project_five/widgets/business/adding_product_widgets/four_step.dart';
import 'package:project_five/widgets/business/adding_product_widgets/second_step.dart';
import 'package:project_five/widgets/business/adding_product_widgets/third_step.dart';
import 'package:project_five/widgets/business/custome_stepper.dart';
class AddProduct extends StatefulWidget {
const AddProduct({Key? key}) : super(key: key);
#override
State<AddProduct> createState() => _AddProductState();
}
class _AddProductState extends State<AddProduct> {
static const _stepIcons = [
Icons.add_circle,
Icons.document_scanner,
Icons.camera_alt_rounded,
Icons.check,
];
static const _titles = ['المنتج', 'تفاصيل', 'الصور', 'نشر'];
var _contnet = [
FirstStep(),
SecondStep(),
ThirdStep(),
Forth()];
var _curStep = 1;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('إضافة منتج'),
centerTitle: true,
),
persistentFooterButtons: [
Row(
children: [
Expanded(
child: ElevatedButton(
child: const Text('التالي'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(16),
),
onPressed: () => setState(() {
if (_curStep < _stepIcons.length) _curStep++;
}),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: ElevatedButton(
child: const Text('رجوع'),
style: ElevatedButton.styleFrom(
primary: Colors.white,
onPrimary: Colors.black,
padding: const EdgeInsets.all(16)),
onPressed: () => setState(() {
if (_curStep > 1) _curStep--;
}),
),
),
],
)
],
body: CustomeStepper(
icons: _stepIcons,
width: MediaQuery.of(context).size.width,
curStep: _curStep,
titles: _titles,
circleActiveColor: Colors.green,
circleInactiveColor: const Color(0xffD5D5D5),
iconActiveColor: Colors.white,
iconInactiveColor: Colors.white,
textActiveColor: Colors.green,
textInactiveColor: const Color(0xffD5D5D5),
content: _contnet,
),
);
}
}
I had the same problem, It would help to see your forms widgets. I will try my best to describe what you need to do.
Your textfields in your forms should be tied to your model class. Example: onChange: Product.title = TextField.value. and you should use initial value with your model properties, example: initialValue: Product.title. I think this way you can retain the state of the inputs in your forms.
As for the second part of your question, the Main widget that is controlling the stepper should have a state variable, such as isCompleted, on the last step you set this variable to 'true' and the main body of the stepper should be in a stack, in your stack you check if "isCompleted" ? Stepper : SummaryWidget.
How are handling Arabic titles for text fields and matching them with your class model properties?
I hope my answer can help!
I'm having trouble displaying a filtered list in my widget. It works up to the point of printing the filtered data in the list as per the query that is passed but not when that exact data needs to be displayed. I believe I will have to update the list with the filtered data every time I type in a query but I just cannot figure out how and where I need to use setState to update that. My code and the outputs are as follows:
Initially, the entire list gets rendered but the moment I type in a query string, the list is supposed to get modified with only the data that matched the query. This is not something that's happening at the moment. The list tends to remain as it is.
However, when I print the filtered data, it seems to work just fine(_searchResult printed in the searchData method below).
[
{product_id: 8, restaurant_name: Mocambo, restaurant_id: 6, product_name: Kaju Paneer, product_description: Tasty yummy paneer gravy dish, product_image: /public/assets/product/lgml5L03-19-41.jpg, product_selling_price: 320},
{product_id: 5, restaurant_name: City Club, restaurant_id: 1, product_name: Palak Paneer, product_description: Tasty silky gravy with goodness of palak, product_image: /public/assets/product/C6pGz101-42-17.jpg, product_selling_price: 180},
{product_id: 4, restaurant_name: City Club, restaurant_id: 1, product_name: Shahi Paneer, product_description: Tasty Paneer main course dish, product_image: /public/assets/product/vgI1dR01-29-18.jpg, product_selling_price: 240}
]
The code:
The method that filters. (Please note that the filtering is performed after the data is fetched from the server. For my convenience, I decided to convert it into a list)
class PopularDishesProvider with ChangeNotifier {
Map<String, dynamic> _dishes = {};
final List<dynamic> _searchDish = [];
List<dynamic> _searchResult = [];
List<dynamic> get searchDish {
return [..._searchDish];
}
List<dynamic> get searchResult {
return [..._searchResult];
}
Future<void> searchData(String query) async {
final url = Uri.parse(baseUrl + 'api/all_products');
final response = await http.get(url);
PopularDishes popularDishes = popularDishesFromJson(response.body); //This method converts the response into Dart model
_dishes = popularDishes.toJson();
_dishes['data'].forEach((value) => _searchDish.add(value));
_searchResult = _searchDish.where((element) {
final name = element['product_name'].toLowerCase();
final searchQuery = query.toLowerCase();
return name.contains(searchQuery);
}).toList();
print(_searchResult);
notifyListeners();
}
}
The widget where this is supposed to be rendered:
class SearchState extends State<Search> {
final _controller = TextEditingController();
bool value = true;
String query = '';
List<dynamic> search = [];
PopularDishesProvider popular = PopularDishesProvider();
#override
void initState() { //This is to make the API Call for the first time
// TODO: implement initState
Provider.of<PopularDishesProvider>(context, listen: false)
.searchData('');
});
super.initState();
}
#override
Widget build(BuildContext context) {
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
final textScale = MediaQuery.of(context).textScaleFactor * 1.2;
final searchProvider = Provider.of<PopularDishesProvider>(context).searchResult;
PopularDishesProvider popular = PopularDishesProvider();
// TODO: implement build
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
elevation: 5,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
// backgroundColor: Colors.green,
titleSpacing: 0,
toolbarHeight: 100,
title: Column(
children: [
Container(
width: double.infinity,
height: 40,
.......
.......
.......
),
Stack(
children: [
Container(
height: 60,
width: double.infinity,
// color: Colors.red,
padding: const EdgeInsets.only(top: 8, left: 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Icon(
Icons.search,
size: 30,
color: Colors.grey,
),
Expanded(
child: Center(
child: Container(
margin:
const EdgeInsets.only(bottom: 6, right: 4),
padding: const EdgeInsets.only(left: 6),
height: 45,
width: width * 0.7,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(14)),
border:
Border.all(color: Colors.grey, width: 2)),
child: Row(
children: [
Flexible(
flex: 9,
fit: FlexFit.tight,
child: Center(
child: TextField(
controller: _controller,
onChanged: (value) async {
setState(() {
query = value;
});
await popular.searchData(value);
},
autofocus: true,
cursorColor: Colors.grey,
style: const TextStyle(
color: Colors.grey, fontSize: 18),
decoration: const InputDecoration(
border: InputBorder.none,
hintText:
'Search By Restaurant or Food',
hintStyle:
TextStyle(color: Colors.grey),
),
),
)),
Flexible(
flex: 1,
fit: FlexFit.tight,
child: InkWell(
onTap: () => Navigator.of(context).pop(),
child: const Icon(Icons.close,
color: Colors.grey),
),
)
],
),
),
),
),
],
),
),
],
)
],
)),
body: Column(
children: [
Expanded(
child: Container(
width: double.infinity,
color: Colors.red,
child: ListView.builder(
itemBuilder: (context, index) => ListTile(
title: Text(searchProvider [index]['product_name'])),
itemCount: searchProvider.length,
),
)
)
],
),
);
}
}
Can someone please help out?
ive created the below Multiselect Chip Widget its using Provider and Listening for changes to the list
the widget creates a List of Choice Chips that allows multiple choice chips to be chosen
class MultiSelectChip extends StatefulWidget {
final Function(List<String>) onSelectionChanged;
MultiSelectChip({this.onSelectionChanged});
#override
_MultiSelectChipState createState() => _MultiSelectChipState();
}
class _MultiSelectChipState extends State<MultiSelectChip> {
List<String> selected = List();
List<Clinic> clinicList = List();
#override
void didChangeDependencies() {
final list = Provider.of<ClinicProvider>(context).clinics;
final clinic = Clinic(
id: null,
name: "All Clinics",
city: null,
suburb: null,
postcode: null,
prate: null,
udarate: null,
goal: null,
uid: null);
clinicList.add(clinic);
selected.add(clinicList[0].name);
list.forEach((clinic) => clinicList.add(clinic));
super.didChangeDependencies();
}
_buildList() {
List<Widget> choices = List();
clinicList.forEach((item) {
choices.add(Padding(
padding: const EdgeInsets.only(left: 5.0, right: 5.0),
child: ChoiceChip(
key: Key("${item.name}"),
shape: selected.contains(item.name)
? RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(0),
),
)
: RoundedRectangleBorder(
side: BorderSide(
color: Color.fromRGBO(46, 54, 143, 1), width: 1.0),
borderRadius: BorderRadius.circular(0.0),
),
label: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.name),
),
onSelected: (value) {
setState(() {
selected.contains(item.name)
? selected.remove(item.name)
: selected.add(item.name);
widget.onSelectionChanged(selected);
});
},
selected: selected.contains(item.name),
selectedColor: Color.fromRGBO(46, 54, 143, 1),
labelStyle:
selected.contains(item.name) ? kChipActive : kChipInActive,
backgroundColor: Colors.transparent,
),
));
});
return choices;
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 8.0, top: 5.0, bottom: 5.0),
child: SizedBox(
height: 50,
width: double.infinity,
child: ListView(
scrollDirection: Axis.horizontal,
children: _buildList(),
),
),
);
}
}
when i click from this Log screen and goto the NewLog screen and Pop Back to the Log Screen
class LogScreen extends StatefulWidget {
static const String id = 'logscreen';
#override
_LogScreenState createState() => _LogScreenState();
}
class _LogScreenState extends State<LogScreen> {
MonthSelector selectedMonth;
List<String> selectedItems = List();
static DateTime now = DateTime.now();
static DateTime end = DateTime(now.year, now.month + 1, 0);
static DateTime start = DateTime(now.year, now.month, 1);
MonthSelector currentMonth = MonthSelector(
monthName: DateFormat("MMMM").format(now),
monthStart: start,
monthEnd: end);
void refreshData(MonthSelector selector) async {
await Provider.of<LogProvider>(context, listen: false)
.getLogs(selector.monthStart, selector.monthEnd);
await Provider.of<LogProvider>(context, listen: false)
.loadTreatments(selector.monthStart, selector.monthEnd);
}
#override
Widget build(BuildContext context) {
final List<LogSummary> list = Provider.of<LogProvider>(context).summary;
final List<FlSpot> chartData = Provider.of<LogProvider>(context).spots;
return Container(
color: Color.fromRGBO(246, 246, 246, 1),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 15,
),
RawMaterialButton(
onPressed: () {
Navigator.pushNamed(context, NewLogScreen.id);
},
constraints: BoxConstraints.tight(Size(60, 60)),
child: Icon(
Icons.add,
color: Color.fromRGBO(255, 255, 255, 1),
size: 30,
),
shape: CircleBorder(),
fillColor: Color.fromRGBO(46, 54, 143, 1),
padding: EdgeInsets.all(15.0),
elevation: 1,
),
SizedBox(
height: 10,
),
Text(
'Add log',
style: kAddLogLabel,
)
],
),
),
]),
list.isEmpty || chartData.isEmpty
? Expanded(
child: Center(
child: Text("No Log Data.."),
),
)
: Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
height: 150,
alignment: Alignment.center,
child: LineChartWidget(
list: chartData,
isDollar: true,
),
),
SizedBox(
height: 10,
),
MultiSelectChip(
onSelectionChanged: (selectedList) async {
setState(() {
selectedItems = selectedList;
});
await Provider.of<LogProvider>(context, listen: false)
.filterLogList(selectedItems);
},
),
MonthSelect(Color.fromRGBO(246, 246, 246, 1),
onMonthSelectionChanged: (selected) {
setState(() {
selectedMonth = selected;
});
selectedMonth == null
? refreshData(currentMonth)
: refreshData(selectedMonth);
}),
Padding(
padding:
const EdgeInsets.only(top: 10, left: 0, right: 0),
child: Container(
width: double.infinity,
height: 1.0,
color: kDividerColor,
),
),
what i am seeing is the Multiselect Chip has the Same List of Items redrawn/added to the list view 3 times, each time i go into the NewLog screen the list keeps growing
im currently using the same Widget across 4 diffrent screens, but for some reason when i navigate to one of the other screen the list resets and displays the orignal items and the duplicate items dissapear
what can i do to prevent this from redrawing, all the time when navigating off the screen
thanks
Have you tried specifying listen: false in Provider.of() used in didChangeDependencies()? It may solve the issue.
However, there can still be a risk. I doubt initialising something there is safe because didChangeDependencies() is called when/whenever a dependency of the State object changes as written in its document. It'd be safer to do it in initState(), or have it done outside only once and its result passed in to MultiSelectChip.