Hi! I'm trying to implement the same functionality duolingo in flutter - flutter

I'm trying to implement the same functionality, where word choices are offered as separate buttons and by clicking on them they are moved to the line, creating a sentence. Just like duolingo.
Example
I started doing it through TextFiled and text, but something tells me that's not a good solution.
How would you implement something like this?
import 'package:flutter/material.dart';
class Screen6_1_3 extends StatefulWidget {
const Screen6_1_3({super.key});
#override
State<Screen6_1_3> createState() => _Screen6_1_3State();
}
class _Screen6_1_3State extends State<Screen6_1_3> {
String textOne = "Bread";
var colorite = Colors.white;
TextEditingController textarea = TextEditingController();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue,
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.blue,
leading: GestureDetector(
child: Icon(
Icons.close,
color: Colors.white,
),
onTap: () {
Navigator.pushNamedAndRemoveUntil(
context, "home", (route) => false);
},
),
),
body: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 70,
height: 50,
decoration: BoxDecoration(
color: const Color.fromARGB(255, 226, 226, 226),
border: Border.all(
color: const Color.fromARGB(255, 123, 123, 123),
width: 2,
),
borderRadius: BorderRadius.circular(10),
),
child: TextField(
enableSuggestions: false,
autocorrect: false,
controller: textarea,
maxLines: 1,
readOnly: false,
decoration: const InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color.fromARGB(255, 123, 123, 123))),
),
onTap: () {
setState(() {
textarea.clear();
textOne = 'Bread';
colorite = Colors.white;
});
},
),
),
],
),
Row(
children: [
SizedBox(
height: 20,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
setState(() {
if (textarea.text.isEmpty) {
insert('Bread');
textOne = 'Bread';
colorite = Colors.transparent;
}
});
},
child: Padding(
padding: const EdgeInsets.all(13.0),
child: Container(
decoration:
BoxDecoration(border: Border.all(color: Colors.white)),
child: Text(
textOne,
style: TextStyle(
color: colorite,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
),
],
),
],
),
);
}
void insert(String content) {
var text = textarea.text;
var pos = textarea.selection.start;
textarea.value = TextEditingValue(
text: text.substring(0, pos) + content + text.substring(pos),
selection: TextSelection.collapsed(offset: pos + content.length),
);
}
}

It will be pretty easy to implement if you use Draggable widgets for the words and DragTarget for the textfields.
Youtube Tutorial: https://www.youtube.com/watch?v=QzA4c4QHZCY
And a great example you might relate to :
https://mobikul.com/draggable-widget-in-flutter/
I'd recommend trying to watch the video first to get an idea about how Draggable widget works and then trying to implement it in your project by taking inspiration from the article that I've provided.

Related

Flutter -Widget Text, not updating when value change even with SetState

I have created a container. His child is a text.
When I tap on the container/text, it display a modal and a Picker.
Then, the user can select a value. Press the confirm button and my text widget should change to display the value selected by the user.
But in my case, it is not updating the value of the text widget. I have used that in the past and it was working very well. But here it is not and I do not see why. I am on this since 8:00. A little help would be appreciated. Many thanks.
int valuePickerUnitSelected =0;
String unitCount = '';
int unitCountInt;
String goal = "";
List <String> unitForHabits = ['Count', 'Minute(s)','Hour(s)','Gramme(s)', 'Pound(s)'];
class AddingHabitDetails extends StatefulWidget {
const AddingHabitDetails({Key key}) : super(key: key);
#override
_AddingHabitDetailsState createState() => _AddingHabitDetailsState();
}
class _AddingHabitDetailsState extends State<AddingHabitDetails> {
BuildContext get ctx => null;
#override
Widget build(BuildContext context) {
return Scaffold(
//drawer: new MyMenu(), //TODO a remettre
appBar: new AppBar(
title: new Text('Habits'),
),
body: Column(
children: [
titleZone('Name'),
textFieldHabits('Habit name', context),
titleZone('Goals'),
FlatButton(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (unitCount.length < 2 )...[
Container(
width: 65,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue,
),
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(20))),
child:
Center(child: Text(
'Time', style: TextStyle(color: Colors.black,))))
]
else
...[
Container(
width: 65,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue,),
color: Colors.blue,
borderRadius: BorderRadius.all(
Radius.circular(20))),
child: Center(
child: Text(
unitCount, style: TextStyle(color: Colors.black,))))
],
],
),
onPressed: () {
setState(() {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return ShowPickerUnite(unitForHabits);
});
},
);
},
),
//ChipGoalV3(ctx),// ChipGoal(),
textFieldHabits('Goals', context),
titleZone('Frequency'),
textFieldHabits('Frequency', context),
titleZone('Time Range'),
titleZone('Reminder'),
titleZone('Habits Term'),
],
),
);
}
}
//########################################################################
class ShowPickerUnite extends StatefulWidget {
List<String> myListUnit;
ShowPickerUnite(this.myListUnit, {Key key}) : super(key: key);
#override
_ShowPickerUniteState createState() => _ShowPickerUniteState(
myListUnit);
}
class _ShowPickerUniteState extends State<ShowPickerUnite> {
List <String> myListUnit;
_ShowPickerUniteState(this.myListUnit);
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
decoration: BoxDecoration(
color: Color(0xffffffff),
border: Border(
bottom: BorderSide(
color: Color(0xffffffff),
width: 0.0,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CupertinoButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 5.0,
),
),
DefaultTextStyle(
style: TextStyle(
fontSize: 16.0,
color: Colors.black,
fontWeight: FontWeight.bold),
child: Text('Select what you want'),
),
// Text('Energy Needed', style: TextStyle(fontSize: 12.0, color: Colors.black),
// ),
CupertinoButton(
child: Text('Confirm'),
onPressed: () {
setState(() {
unitCount = unitForHabits[valuePickerUnitSelected];
print(unitCount);
});
Navigator.of(context).pop();
},
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 5.0,
),
),
],
),
),
Container(
//width: 360,
height: 200,
decoration:BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
child: CupertinoPicker(
backgroundColor: Colors.white ,
useMagnifier: true,
magnification: 1.3,
scrollController: FixedExtentScrollController(initialItem: 0),
itemExtent: 25,
children: [
for (String name in myListUnit)
Center(
child:Text(name)),
],
onSelectedItemChanged: (value) {
setState(() {
valuePickerUnitSelected = value;
// taskEnergy = myListEnergy[valuePickerEnergySelected];
// taskNewValue ['task_Energy'] = taskEnergy;
});
}))
]);
}
}
Widget inputNameHabit (){
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text ('Name Habits',style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold),),
);
}
Widget titleZone (String _titleName){
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text ( _titleName,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold),),
],
),
);
}
Widget textFieldHabits (String item,context){
return TextField(
decoration: InputDecoration(
hintText: item,
filled: true,
fillColor: Colors.grey[300],
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(50)
),
),
onTap: (){
Navigator.push(context,
MaterialPageRoute(
builder: (context) => HabitGoalUnitSelection(), //TODO MODIFIER route selon source
),
);
},);
}
Wait for the dialog to finish and then call setState to update the UI.
Modify this way
onPressed: () async {
await showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return ShowPickerUnite(unitForHabits);
});
setState(() {});

I want to implement OTP verification screen without any package

I want to implement OTP verification screen without any package.
when i entered the number it should move to next input field
I m using this code in my current project take a refrence this will help
class Otp extends StatefulWidget {
final String? phnNumber;
final String ? code;
String? from;
Otp({Key ?key, this.phnNumber, this.from, this.code}) : super(key:
key);
#override
_OtpState createState() => _OtpState();
}
class _OtpState extends State<Otp> {
double ? height ;
double ? width;
TextEditingController ? contrller1;
TextEditingController ? contrller2;
TextEditingController ? contrller3;
TextEditingController ? contrller4;
SendOtpRequest resend = SendOtpRequest();
SharedPreferences ? prefs;
getSharedPreferences () async
{
prefs = await SharedPreferences.getInstance();
}
String Code = "";
#override
void initState() {
// TODO: implement initState
super.initState();
contrller1 = TextEditingController();
contrller2 = TextEditingController();
contrller3 = TextEditingController();
contrller4 = TextEditingController();
getSharedPreferences();
}
#override
Widget build(BuildContext context) {
height= MediaQuery.of(context).size.height;
width = MediaQuery.of(context).size.height;
final verifyprovider = Provider.of<PostDataProvider>(context);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
toolbarHeight:height! * 0.07802345,
titleSpacing: 0,
backgroundColor: HexColor("#18263d"),
automaticallyImplyLeading: false,
leading: Padding(
padding: const EdgeInsets.only(left: 8.0,),
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
color: Colors.transparent,
child: Image.asset("assets/images/back_ic-1.png")),
),
),
// SizedBox(width: width!*0.001234,),
title:Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: height!/15,
width: height!/15,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 2,
color:HexColor("#fc4f00"),
)),
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Container(
height: height!/11,
width: height!/11,
decoration: BoxDecoration(
image: const DecorationImage(
image:
AssetImage("assets/images/home_logo.png"),
fit: BoxFit.fill
),
shape: BoxShape.circle,
border: Border.all(
width: 1,
color:HexColor("#fc4f00"),
)),
),
),
),
SizedBox(width: width! * 0.04234,),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text("Verification",
style: GoogleFonts.oswald(fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: width! * 0.03345
),),
),
],
) ,
),
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 24, horizontal: 32),
child: Column(
children: [
Text("We have send verification code on your mobile number",
style: GoogleFonts.oswald(fontStyle: FontStyle.normal,
fontSize: width!*0.0234,
color: HexColor("#8b8b8b")),
),
SizedBox(height: height!/38,),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_textFieldOTP(first: true, last: false,controllerr:
contrller1),
_textFieldOTP(first: false, last: false,controllerr:
contrller2),
_textFieldOTP(first: false, last: false,controllerr:
contrller3),
_textFieldOTP(first: false, last: true, controllerr:
contrller4),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap:() {
resend.phoneNumber= widget.phnNumber;
resend.countryCode = widget.code;
verifyprovider.resendOtp(context,
jsonEncode(resend));
},
child: Text("Resend OTP?",
style: GoogleFonts.oswald(fontStyle:
FontStyle.normal,
fontSize: width!*0.0234,
color: HexColor("#fc4f00")),
),
),
),
SizedBox(height: height!/28,),
GestureDetector(
onTap: (){
if(contrller1!.text.isNotEmpty&&
contrller2!.text.isNotEmpty&&contrller3!.
text.isNotEmpty&&contrller4!.text.isNotEmpty){
verifyOtpRequest verify = verifyOtpRequest();
verify.phoneNumber = widget.phnNumber;
verify.otp=
contrller1!.text+contrller2!.
text+contrller3!.text+contrller4!.text;
verifyprovider.otpVerification(context,
jsonEncode(verify), widget.from);
}else{
CommonUtils.showToast(msg: "Please fill all the
fields ");
}
},
child: Container(
height: height!/18,
width: width,
decoration: BoxDecoration(
color: HexColor("#fc4f00"),
borderRadius: BorderRadius.circular(10)
),
child: Center(
child: Text("Verify",style: TextStyle(
color: Colors.white,
fontSize: width!*0.02345
),),
)
),
),
],
),
],
),
),
),
);
}
Widget _textFieldOTP({bool ? first, last,
TextEditingController ?
controllerr}) {
return Container(
height:height!/12 ,
child: AspectRatio(
aspectRatio: 1.0,
child: TextField(
controller: controllerr,
autofocus: true,
onChanged: (value) {
if (value.length == 1 && last == false) {
FocusScope.of(context).nextFocus();
}
if (value.length == 0 && first == false) {
FocusScope.of(context).previousFocus();
}
},
showCursor: false,
readOnly: false,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(
counter: Offstage(),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(width: 2, color: Colors.black54),
borderRadius: BorderRadius.circular(12)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(width: 2, color: Colors.black54),
borderRadius: BorderRadius.circular(12)),
),
),
),
);
}
}
When the length of the input data reaches one, you will have to change the text field focus node.
For Example
If you are in the first field, and you enter a number field one focus should be lost, and field two should be in focus. This can be done, by requestFocus.
This article will of help for you: Flutter Focus

Hello, I want to show the padding part at the bottom of my code on the screen with a delay of 10 seconds. How can I do it?

I want to show the padding part at the bottom of my code on the screen with a delay of 10 seconds. How can I do it?
My Code :
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_clipboard_manager/flutter_clipboard_manager.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'URL Shortener',
theme: ThemeData(
primarySwatch: Colors.purple,
),
home: StartPage(),
);
}
}
class StartPage extends StatefulWidget {
#override
_StartPageState createState() => _StartPageState();
}
class _StartPageState extends State<StartPage> {
bool visibilityTag = false;
void _changed(bool visibility, String field) {
setState(() {
if (field == "tag") {
visibilityTag = visibility;
}
});
}
final GlobalKey<ScaffoldState> _globalKey = GlobalKey<ScaffoldState>();
String shortUrl = "";
String value = "";
String buttonText = "COPY!";
bool isChanged = true;
TextEditingController urlcontroller = TextEditingController();
getData() async {
var url = 'https://api.shrtco.de/v2/shorten?url=${urlcontroller.text}';
var response = await http.get(url);
var result = jsonDecode(response.body);
if (result['ok']) {
setState(() {
shortUrl = result['result']['short_link'];
});
} else {
print(response);
}
}
copy(String url) {
FlutterClipboardManager.copyToClipBoard(url).then((value) {});
}
buildRow(String data, bool original) {
return SingleChildScrollView(
child: original
? Container(
alignment: Alignment.center,
child: Text(
data,
))
: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
data,
),
ElevatedButton(
child: Text(buttonText),
style: ElevatedButton.styleFrom(
primary: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
minimumSize: Size(300, 40),
),
onPressed: () {
copy(shortUrl);
setState(() {
if (isChanged == true) {
buttonText = "COPIED!";
}
});
},
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[300],
body: ListView(
children: [
SvgPicture.asset(
'assets/logo.svg',
),
SvgPicture.asset(
'assets/illustration.svg',
),
Center(
child: Text(
"Let's get started!",
style: TextStyle(
fontSize: 20,
color: Color.fromRGBO(53, 50, 62, 10),
fontWeight: FontWeight.bold),
),
),
Center(
child: SizedBox(
width: 200,
height: 60,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Paste your first link into the field to shorten it",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
color: Color.fromRGBO(76, 74, 85, 10),
fontWeight: FontWeight.bold)),
),
),
),
SizedBox(
height: 130,
child: Stack(
alignment: Alignment.center,
children: [
Container(
alignment: Alignment.centerRight,
color: Color.fromRGBO(59, 48, 84, 1),
child: SvgPicture.asset(
'assets/shape.svg',
color: Color.fromRGBO(75, 63, 107, 1),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 300,
height: 40,
child: TextField(
onChanged: (text) {
value = "URL : " + text;
},
controller: urlcontroller,
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(10.0),
),
borderSide: BorderSide(
width: 0,
style: BorderStyle.none,
),
),
fillColor: Colors.white,
filled: true,
hintText: 'Shorten a link here ...'),
),
),
SizedBox(
height: 10,
),
SizedBox(
width: 300,
child: ElevatedButton(
onPressed: getData,
style: ElevatedButton.styleFrom(
primary: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
minimumSize: Size(60, 40),
),
child: Text('SHORTEN IT!'),
),
),
],
),
],
),
),
Padding(
padding: const EdgeInsets.all(13.0),
child: Container(
color: Colors.white,
width: double.infinity,
child: Column(
children: [
SizedBox(
height: 10,
),
buildRow(value, true),
buildRow(shortUrl, false),
],
),
),
)
],
),
);
}
}
Hello, I want to show the padding part at the bottom of my code on the screen with a delay of 10 seconds. How can I do it?
Hello, I want to show the padding part at the bottom of my code on the screen with a delay of 10 seconds. How can I do it?
Hello, I want to show the padding part at the bottom of my code on the screen with a delay of 10 seconds. How can I do it?Hello, I want to show the padding part at the bottom of my code on the screen with a delay of 10 seconds. How can I do it?
On State create a bool like
bool showCopyButton = false;
Change to true at
SizedBox(
width: 300,
child: ElevatedButton(
onPressed: ()async {
print("Button Click");
await getData();
setState(() {
showCopyButton = true;
});
},
style: ElevatedButton.styleFrom(
primary: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
minimumSize: Size(60, 40),
),
child: Text('SHORTEN IT!'),
),
),
And wrap with Visibility Like
Visibility(
visible: showCopyButton,
child: Padding(
padding: const EdgeInsets.all(13.0),
child: Container(
color: Colors.white,
width: double.infinity,
child: Column(
children: [
SizedBox(
height: 10,
),
buildRow("Text ", true),
buildRow("asdad", false),
],
),
),
),
)
or you can just if like
if (showCopyButton)
Padding(
padding: const EdgeInsets.all(13.0),
child: Container(
color: Colors.white,
width: double.infinity,
child: Column(
children: [
SizedBox(
height: 10,
),
buildRow("Text ", true),
buildRow("asdad", false),
],
),
),
),
Btw you need to use await before making it true.
Hope this helps:
import 'package:flutter/material.dart';
class Screen extends StatefulWidget {
#override
_ScreenState createState() => _ScreenState();
}
class _ScreenState extends State<Screen> {
bool _paddingVisible = false;
#override
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 10), () {
// Check if mounted == true, because the screen might closed
// before 10 seconds pass
if (mounted) {
// Setting padding visibility to true after 10 seconds
setState(() {
_paddingVisible = true;
});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
// All your widgets
// When _paddingVisible becomes true it will be displayed
if (_paddingVisible) SizedBox(height: 10),
],
),
);
}
}

Flutter Provider.of and search

I am a beginner in flutter and app development. I have a problem. I am using Provider.of in order to get my data. I am getting data and showing it in ListView.builder with no problem. But I want to make a search on my list.
Please refer to code below
class RecipeList extends StatefulWidget {
#override
_RecipeListState createState() => _RecipeListState();
}
class _RecipeListState extends State<RecipeList> {
List<Recipe>showList =List();//creating my list of searched data
#override
Widget build(BuildContext context) {
//getting my recipe list in order to show them
final recipes = Provider.of<List<Recipe>>(context);
showList=recipes;
final user = Provider.of<User>(context);
String _image;
Widget myImage(int index,)
{
if(recipes[index].image == ''){
return Image.asset('images/no_image.jpg');
}
else{
return
FadeInImage.assetNetwork(
width: 300,
height: 250,
placeholder: 'images/loading.webp',
image: recipes[index].image,
);
}
}
return StreamBuilder<UserData>(
stream:DatabaseService(uid: user.uid).userData,
builder: (context,snapshot){
if(snapshot.hasData) {
UserData userdata = snapshot.data;
if (userdata.is_admin == true) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.blue[200], Colors.orange[100]])),
child: Scaffold(
appBar: AppBar(
title: Text('Recipes'),
backgroundColor: Colors.transparent,
elevation: 0,
),
backgroundColor: Colors.transparent,
body: Column(
children: <Widget>[
Material(
elevation: 0,
color: Colors.transparent,
child: TextField(
onChanged: (val) {
val = val.toLowerCase();
setState(() {
showList = recipes.where((recipe){
var title = recipe.name.toLowerCase();
return title.contains(val);
}).toList();
});
},
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(25.0)))),
),),
SizedBox(height: 15,),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: showList.length,
itemBuilder: (context, index) {
if (recipes[index].image == null) {
String _image = 'images/new.png';
}
else {
_image = recipes[index].image;
}
// print(recipes[index].image);
return Column(
children: <Widget>[
SlimyCard(
color: Colors.teal[200],
width: 300,
topCardHeight: 350,
bottomCardHeight: 300,
borderRadius: 15,
topCardWidget: Column(
children: <Widget>[
Text(recipes[index].name[0]
.toUpperCase() +
recipes[index].name.substring(1),
style: TextStyle(
fontSize: 35,
color: Colors.white,
fontWeight: FontWeight.bold,
),),
ClipRRect(borderRadius: BorderRadius
.circular(25.0),
child: myImage(index)
),
// Image.network('https://www.bbcgoodfood.com/sites/default/files/recipe-collections/collection-image/2013/05/chorizo-mozarella-gnocchi-bake-cropped.jpg')),
],
),
bottomCardWidget: SingleChildScrollView(
child: Column(
children: <Widget>[
Text('Ingredients',
style: TextStyle(
fontSize: 25,
color: Colors.white
),),
SizedBox(height: 5,),
Text(recipes[index].ingredients,
style: TextStyle(
fontSize: 16
),),
SizedBox(height: 20,),
Text('Recipe',
style: TextStyle(
fontSize: 25
,
color: Colors.white
),),
SizedBox(height: 5,),
Text(recipes[index].recipe,
style: TextStyle(
fontSize: 16
),),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius
.circular(7.0),
//side: BorderSide(color: Colors.orange)
),
color: Color.fromRGBO(
233, 217, 108, 1),
onPressed: () async {
final CollectionReference recipecollection = Firestore
.instance.collection(
'recipe');
await recipecollection.document(
recipes[index].id).delete();
StorageReference firestoreStorageref = await FirebaseStorage
.instance
.getReferenceFromUrl(
recipes[index].image);
firestoreStorageref.delete();
},
child: Text(
'Delete'
),
)
],
),
),
slimeEnabled: false,
),
SizedBox(height: 25,)
],
);
},
)),
],
)
),
);
}
I want to show this list on the search and modify it. first I fill it with data from the provider.
I have created a TextField for Search the onChanged method filters the typed value and returns a list. When I print in onChanged function it is working.
I am showing my list with ListView, when I print the size of showList in onChanged function, it filters and gives the right value but when I use it for itemCount it never changes
You can use searchable_dropdown instead of the TextField. You can assign the list to it and it will search the list based on the to string method so you have to override it.
Refer the link to the dependency: https://pub.dev/packages/searchable_dropdown.

Why does a change in state of a child bottom sheet trigger a rebuild of parent widget?

I have a Scaffold screen (ListsScreen).
Which has a Button(AddNewListButton) that opens up a Modal Bottom Sheet (ListScreenBottomSheetWidget).
Bottom Sheet has TextField (ListTitleInputTextFieldWidget).
When i tap the TextField to open the keyboard, the parent screen rebuilds itself, due to which ofcourse all its child widgets are rebuilt as well.
Why is this happening? I was under the impression that state changes only rebuild themselves or their children, not their parents. And i also added const constructors almost everywhere to avoid rebuilds but this is still happening.
The Parent ListsScreen:
class ListsScreen extends StatelessWidget {
const ListsScreen();
static const routeName = '/lists-screen';
#override
Widget build(BuildContext context) {
final user = Provider.of<AuthProvider>(context, listen: false).getUser;
print('stateless rebuilding');
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Text(
'${user['name']}\'s Lists',
style: TextStyle(
color: Theme.of(context).primaryColorLight,
),
),
actions: <Widget>[
const SignOutButton(),
],
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
SizeConfig.smallDevice
? const SizedBox(
height: 30,
)
: const SizedBox(
height: 40,
),
SizeConfig.smallDevice
? Text(
'Welcome to TODOS',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.grey[700],
),
)
: Text(
'Welcome to TODOS',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
color: Colors.grey[700],
),
),
SizeConfig.smallDevice
? const SizedBox(
height: 30,
)
: const SizedBox(
height: 40,
),
const AddNewListButton(),
SizeConfig.smallDevice
? const SizedBox(
height: 30,
)
: const SizedBox(
height: 40,
),
const UserListsListViewWidget(),
],
),
),
),
);
}
}
class SignOutButton extends StatelessWidget {
const SignOutButton();
Future<void> _submitRequest(BuildContext context) async {
_showLoadingAlert(context);
try {
await Provider.of<AuthProvider>(context, listen: false)
.submitLogOutRequest();
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed(LoginScreen.routeName);
} on HttpExceptions catch (error) {
Navigator.of(context).pop();
_showErrorDialogue(error.getErrorList, context);
}
}
void _showErrorDialogue(List<dynamic> errorMessages, BuildContext context) {
showDialog(
context: context,
builder: (ctx) => Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: ErrorListWidget(errorMessages: errorMessages),
),
);
}
void _showLoadingAlert(BuildContext context) {
showDialog(
context: context,
builder: (ctx) => const LoadingWidget(),
);
}
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () => _submitRequest(context),
child: Row(
children: <Widget>[
Text(
'Sign Out',
style: TextStyle(
color: Theme.of(context).primaryColorLight,
),
),
Icon(
Icons.exit_to_app,
color: Theme.of(context).primaryColorLight,
),
],
),
);
}
}
class AddNewListButton extends StatelessWidget {
const AddNewListButton();
void _modalBottomSheetMenu(BuildContext context) {
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
isScrollControlled: true,
builder: (builder) {
return const ListScreenBottomSheetWidget();
},
);
}
#override
Widget build(BuildContext context) {
return RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
elevation: 10,
color: Theme.of(context).primaryColor,
onPressed: () => _modalBottomSheetMenu(context),
child: Text(
'+ Add List',
style: TextStyle(
color: Colors.white,
fontSize: SizeConfig.smallDevice ? 10 : 15,
),
),
);
}
}
The Modal Bottom Sheet:
import 'package:flutter/material.dart';
import 'package:todo_spicotech/helpers/size_config.dart';
class ListScreenBottomSheetWidget extends StatelessWidget {
const ListScreenBottomSheetWidget();
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
currentFocus.unfocus();
},
child: Container(
margin: const EdgeInsets.all(20.0),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Material(
borderRadius: BorderRadius.all(Radius.circular(15)),
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizeConfig.smallDevice
? Text(
'Create a new List',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.grey[700],
),
)
: Text(
'Create a new List',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
color: Colors.grey[700],
),
),
SizeConfig.smallDevice
? const SizedBox(
height: 20,
)
: const SizedBox(
height: 30,
),
const ListTitleInputTextFieldWidget(),
SizeConfig.smallDevice
? const SizedBox(
height: 20,
)
: const SizedBox(
height: 30,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
InkWell(
borderRadius: BorderRadius.circular(5),
onTap: () {
Navigator.of(context).pop();
},
child: Ink(
padding: EdgeInsets.all(10),
child: const Text('CANCEL'),
),
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
elevation: 10,
color: Theme.of(context).primaryColor,
onPressed: () {},
child: Text(
'Create',
style: TextStyle(
color: Colors.white,
fontSize: SizeConfig.smallDevice ? 10 : 15,
),
),
),
],
),
],
),
),
),
),
);
}
}
class ListTitleInputTextFieldWidget extends StatefulWidget {
const ListTitleInputTextFieldWidget();
#override
_ListTitleInputTextFieldWidgetState createState() => _ListTitleInputTextFieldWidgetState();
}
class _ListTitleInputTextFieldWidgetState extends State<ListTitleInputTextFieldWidget> {
#override
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.lightBlue,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.lightBlue,
),
),
labelText: 'List Title',
contentPadding: EdgeInsets.all(10),
),
);
}
}
when you call showModalBottomSheet , it actually use Navigator inside
return Navigator.of(context, rootNavigator: useRootNavigator).push(_ModalBottomSheetRoute<T>(
builder: builder,
source code of showModalBottomSheet https://github.com/flutter/flutter/blob/17079f26b54c8517678699a0cefe5f7bfec67b3f/packages/flutter/lib/src/material/bottom_sheet.dart#L635
Flutter teams' reply of issue Pages on Navigator stack rebuild when a new page is pushed https://github.com/flutter/flutter/issues/11655#issuecomment-348287396
This is working as intended. In general, you should assume that all widgets can rebuild at any time, that they don't is mostly just an optimisation.
In particular, routes will rebuild because their navigator state has changed so they might need to update how they draw back buttons and the like.