I have a screen that consist of ExpansionTile and as we know that ExpansionTile has children property. The thing that I would like to do is I need to catch the trigger click of ExpansionTile, so when user click the ExpansionTile it will run FutureBuilder to get a data from API, is there a way to do that. Because until now... ExpansionTile always runs together with the children
Here is part of the code
ExpansionTile(
children:[] //here.. I would like to call the data from api, when user click the expansiontile
title: Text(
"${dataAll["data"][i]["lowongan"]}",
style: GoogleFonts.poppins(
fontWeight: FontWeight.w600,
color: isHovered ||
listLokerModel.value
.lokerState[i]
? dark_button
: Colors.white,
fontSize: 20)),
subtitle: Text(
"${dataAll["data"][i]["start"]} - ${dataAll["data"][i]["end"]}",
style: GoogleFonts.poppins(
color: isHovered ||
listLokerModel.value
.lokerState[i]
? dark_button
: Colors.white,
fontSize: 16)),
trailing: Icon(
Icons.keyboard_arrow_down_sharp,
color: isHovered ||
listLokerModel
.value.lokerState[i]
? dark_button
: Colors.white,
))
Here's simple solution to it.
Inside ExpansionTile, children add FutureBuilder, refer below code, you will get an idea.
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ExpansionTile(title:Text('hello'),
children:[
FutureBuilder(
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If we got an error
if (snapshot.hasError) {
return Center(
child: Text(
'${snapshot.error} occured',
style: TextStyle(fontSize: 18),
),
);
// if we got our data
} else if (snapshot.hasData) {
// Extracting data from snapshot object
final data = snapshot.data as String;
return Center(
child: Text(
'$data',
style: TextStyle(fontSize: 18),
),
);
}
}
// Displaying LoadingSpinner to indicate waiting state
return Center(
child: CircularProgressIndicator(),
);
},
// Future that needs to be resolved
// inorder to display something on the Canvas
future: getData(),
),
]);
}
Future<String> getData() {
return Future.delayed(Duration(seconds: 2), () {
return "I am data";
// throw Exception("Custom Error");
});
}
}
Related
I am trying to display a list using a list title but would like to add a text with a click event to the top of the list.
How could I do it?
I would be very grateful for the help.
I am trying to display a list using a list title but would like to add a text with a click event to the top of the list.
How could I do it?
I would be very grateful for the help.
Code and image::
Widget StatefulBuilderSuggestions(BuildContext context ,List <SearchDelegateModel> historialMenuPrincipal){
return Container(
child:StatefulBuilder(
builder:(context,setState)
{
return Container(
child: ListView.builder(
itemCount: historialMenuPrincipal.length,
itemBuilder: (context,i)
{
contentPadding: EdgeInsets.symmetric(vertical: 12,horizontal: 16);
return
ListTile(
subtitle: Text(historialMenuPrincipal[i] == null ? "no data" :historialMenuPrincipal[i].email,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 20,
fontWeight: FontWeight.bold,
),
),
title: Text(historialMenuPrincipal[i] == null ? "no data" :historialMenuPrincipal[i].contrasena,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 20,
fontWeight: FontWeight.bold,
),
),
trailing: historialMenuPrincipal[i] != null || historialMenuPrincipal.isEmpty
? IconButton(
icon: Icon(Icons.cancel,color: Colors.black,),
onPressed: () {
setState(() {
historialMenuPrincipal.remove(historialMenuPrincipal[i]);
});
},): null
);
}
),
);
}
)
);
}
use a column with 2 parts, title as a textbutton and above the list
Container(
child: Column(
children: [
TextButton(
onPressed: () {
hideList = !hideList;
},
child: Text("Title"),
),
hideList ? ListView() : Container(),
],
))
something like this
In order to add a Text widget above a list, both widgets should be inside a column and important thing to note is that ListView widget must be inside a Expanded widget and for the text widget to be clickable, use GestureDetector.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
padding: const EdgeInsets.all(16),
child: Column(
children: [
GestureDetector(
onTap: () {}, child: const Text("Some text above the list")),
Expanded(
child: ListView.builder(
itemCount: 50,
itemBuilder: (context, i) {
return Container(
margin: const EdgeInsets.all(16),
color: Colors.pinkAccent,
child: Text("$i"));
},
),
),
],
),
),
);
}
class _SimpleDialogState extends State<SimpleDialog> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _textEditingController = TextEditingController();
#override
void initState() {
super.initState();
baseurl = Prefs().geturlBase();
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Base URL'),
content: Form(
key: _formKey,
child: TextFormField(
keyboardType: TextInputType.multiline,
maxLines: null,
//controller: _textEditingController,
initialValue: baseurl,
onChanged: (val) {
setState(() {
baseurl = val;
print(baseurl);
});
},
decoration: InputDecoration(
hintText: "Please Enter Base Url",
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(15)),
),
validator: (value) {
// return value!.isNotEmpty ? null : "Enter Base Url";
return Uri.parse(value.toString()).host == ''
? "Enter Base Url"
: null;
},
)),
actions: <Widget>[
Center(
child: InkWell(
onTap: () {
buildShowDialog(context);
if (_formKey.currentState!.validate()) {
baseurl = baseurl.trim();
checkBaseUrl(baseurl, context);
// Navigator.of(context).pop();
print('baseurl=====base------$baseurl');
}
},
child: Container(
width: 100,
height: 40,
padding: EdgeInsets.all(12.0),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(20.0),
),
child: Text(
"Connect",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
),
)
],
);
}
}
This is a dialog box where I can change my baseURL. Now, I need history when I touch textfield, history should appear and I can able to select the data from history to text field and also able to delete history, not all history particular data from that list. how can I able to achieve this kinda logic??.
An input_history_text_field widget is automatically saved and suggested as you type.
How do I ensure that a user does not select the same security question twice by hiding the initially selected question from appearing in the second dropdown button and vice versa in flutter?. i am making a request to the same api for the questions.
Updated the question with some code snippets. Thanks
Container(
height: 60,
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.black,
width: 1),
borderRadius: BorderRadius.circular(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton(
hint: Padding(
padding: const EdgeInsets.only(left:
20.0),
child: Text(
"Security Question Two",
style: TextStyle(
color: Colors.black,
fontSize: 16,
letterSpacing: 0.3,
fontWeight: FontWeight.w300),
),
),
itemHeight: 100,
isExpanded: true,
value: dropDownSecurityQuestionTwo,
icon: Padding(
padding: const EdgeInsets.only(right:
10.0),
child:
Icon(Icons.keyboard_arrow_down_outlined),
),
iconEnabledColor: Colors.black,
iconSize: 30,
style: TextStyle(
color: Colors.black,
),
items: questions.map((value) {
return DropdownMenuItem(
value: value['ID'].toString(),
child: Padding(
padding: const EdgeInsets.only(left:
20.0),
child: Text(
value['question'].toString(),
),
),
);
}).toList(),
onChanged: (newValue) async {
setState(() {
dropDownSecurityQuestionTwo =
newValue.toString();
print(dropDownSecurityQuestionTwo);
checkSelectedQuestion();
});
},
),
),
),
void checkSelectedQuestion(){
List newQuestions = [];
for(int i = 0; i<questions.length; i++){
print(questions[i]['ID']);
questions.removeWhere((value) => value['ID'] ==
int.parse(dropDownSecurityQuestionOne!) );
newQuestions.add(questions);}
setState(() {
questions = newQuestions ;
});}
You can add a where filter to the mapping of items to each DropDownButton, depending on the selected value of the other DropDownButton. As a result of setState, the items will be recreated if anything is selected in the other DropDownButton.
Note: This is easy to implement, but not very efficient. Items will be created and filtered every time. It will work perfectly with few items, but if you would like to do something like this with many items, you might need a more efficient approach. For example keep two items lists, and only add / remove what is affected.
Check this code and adopt it to your case:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyPage(),
);
}
}
class MyPage extends StatefulWidget {
const MyPage({Key? key}) : super(key: key);
#override
State<MyPage> createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
String? _selected1;
String? _selected2;
final List<String> _set = ['Alpha', 'Bravo', 'Charlie'];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: [
DropdownButton<String>(
value: _selected1,
onChanged: (String? newValue) {
setState(() {
_selected1 = newValue!;
});
},
items: _set
.map((value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
})
.where((e) => _selected2 == null || e.value != _selected2)
.toList()),
DropdownButton<String>(
value: _selected2,
onChanged: (String? newValue) {
setState(() {
_selected2 = newValue!;
});
},
items: _set
.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
})
.where((e) => _selected1 == null || e.value != _selected1)
.toList()),
]),
),
),
);
}
}
I cannot remove the 'StarterDayTile' in the line that I say dayList.remove(StarterDayTile());
This is where I originally took the screenshot from:
Apparently I have to add lots more details because my post is mostely code and I don't know what to add so I am now just writing random stuff to pass the bot who is preventing me from posting I hope this works now.
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:moody/data_screen.dart';
import 'package:moody/starter_day_tile.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:page_transition/page_transition.dart';
import 'day_tile.dart';
class HomeScreen extends StatefulWidget {
final String displayDate;
final String displayEmoji;
final int displayHapp;
HomeScreen({this.displayHapp, this.displayDate, this.displayEmoji});
#override
_HomeScreenState createState() => _HomeScreenState();
}
List<Widget> dayList = [];
class _HomeScreenState extends State<HomeScreen> {
#override
void initState() {
super.initState();
if (widget.displayEmoji != null && widget.displayDate != null && widget.displayHapp != null){
print('2');
setState(() {
dayList.insert(0, DayTile(date: widget.displayDate, emoji: widget.displayEmoji, happ: widget.displayHapp));
});
}
if (dayList.length == 0){
dayList.add(StarterDayTile());
}
else if (dayList.length == 2){
dayList.remove(StarterDayTile());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.indigoAccent,
onPressed: () {
FocusScope.of(context).requestFocus(FocusNode()); // Keyboard down
Navigator.pushReplacement(
context,
PageTransition(
type: PageTransitionType.rightToLeftWithFade,
child: DataScreen()));
},
child: Icon(Icons.message),
),
backgroundColor: Colors.white12,
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: Hero(
tag: 'hero1',
child: AppBar(
elevation: 15,
backgroundColor: Colors.indigoAccent,
automaticallyImplyLeading: false,
title: Center(
child: Text(
'Moody',
style: TextStyle(fontFamily: 'Baloo', fontSize: 35),
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(20),
),
),
leading: Padding(
child: IconButton(icon: Icon(Icons.menu), onPressed: () {}),
padding: EdgeInsets.only(right: 0, left: 10),
),
actions: <Widget>[
Padding(
padding: EdgeInsets.only(right: 10, left: 0),
child: IconButton(
icon: Icon(Icons.pie_chart),
onPressed: () {},
)),
],
),
),
),
body: ListView.builder(
reverse: false,
scrollDirection: Axis.vertical,
itemCount: dayList.length,
itemBuilder: (context, index) {
return dayList[index];
},
),
);
}
}
Try this; since it is the first object you want to remove
dayList.removeAt(0);
Solved it!!
I made a bool called startDayTileExists and made it a global variable and then when I added the StarterDayTile to the list I made it equal true and then I changed the dayList.remove() to a dayList.removeAt(1); but made an extra requirement in the if that startDayTileExists had to equal true then in the if I removed it and made startDayTileExists equal to false again.
Thanks for all your help!
Question updated apr 10:
Hi!
I'm still stuck and can't get this to work :(
I'm trying to make an app where the user will answer a total of 3 questions before he's navigated to a result-screen.
To show the progress of the questions there will be 3 colored containers in a row. The row will initally be for example blue but when the user answers correct - the container for that question will turn green, and if answer is incorrect the container will turn red.
I could really use some further help here.
Below I have made the code as simple as I can with different colors just to show the different items in the list.
Right now it works fine with the first question, but then it kind of stops.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'listing 4',
theme: ThemeData(primarySwatch: Colors.blue),
home: FirstScreen(),
);
}
}
class FirstScreen extends StatefulWidget {
#override
_FirstScreenState createState() => _FirstScreenState();
}
class _FirstScreenState extends State<FirstScreen> {
int sum = 5;
String userAnswer;
String correction = "";
List<Color> colors = [Colors.blue, Colors.amber, Colors.pink];
submitPressed(int index) {
if (userAnswer == sum.toString()) {
setState(() {
correction = sum.toString();
colors[index] = Colors.green;
});
} else {
colors[index] = Colors.red;
}
}
Widget myListBuilder() {
return ListView.builder(
itemCount: 3,
itemBuilder: buildContainer,
);
}
Widget buildContainer(BuildContext context, int index) {
return Container(
child: Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Container(
height: 20.0,
width: 15.0,
decoration: BoxDecoration(
color: colors[index], //this is the important line
borderRadius: BorderRadius.all(Radius.circular(8.0))),
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Listing 4'),
),
body: Container(
child: Center(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10.0),
child: Text('Correct answer is 5',
style: TextStyle(fontSize: 20.0)),
),
Container(
width: 50.0,
child: TextField(
textAlign: TextAlign.center,
autofocus: true,
keyboardType: TextInputType.number,
onChanged: (val) {
userAnswer = val;
},
),
),
RaisedButton(
child: Text('Submit'),
onPressed: () {
submitPressed(0);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
buildContainer(context, 0),
buildContainer(context, 1),
buildContainer(context, 2)
],
),
],
),
),
),
);
}
}
Ok, I'm going to assume a few things within this answer, so change them as necessary. The colours you are going to use are Colors.blue for the default color, Colors.green for correct, and Colors.red for incorrect.
You would first initialise a List of colors, all of which will be blue as that is the default color:
List<Color> colors = [Colors.blue, Colors.blue, Colors.blue ..... Colors.blue]
//You will write Colors.blue ten times as there are 10 boxes.
I'm going to assume that you use a ListView.builder here, as you haven't specified it in your code example. You would build your ListView as such:
//Place this within your widget tree
ListView.builder(
itemBuilder: buildContainer,
itemCount: 10,
);
You will then need to modify your buildContainer method as the itemBuilder parameter requires a method to take context and index and output a widget, therefore:
Widget buildContainer(BuildContext context, int index) {
return Container(
child: Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Container(
height: 20.0,
width: 15.0,
decoration: BoxDecoration(
color: colors[index], //this is the important line
borderRadius: BorderRadius.all(Radius.circular(8.0))
),
),
)
);
}
This will then create 10 boxes that each have gotten their colour from their position within the list of colors created earlier. Now you just have to change the color when they are finished. Using your code example:
if (userAnswer == widget.sum.toString()) {
setState(() {
correction = widget.sum.toString();
//Here we will instead set the specific color in the array
colors[index] = Colors.green;
});
} else {
correction = widget.sum.toString();
colors[index] = Colors.red;
}
The only thing you need to do is make sure the function when you click next takes a variable which is the index of the questions, i.e. the question number you are on.