CircularProgressIndicator code in appBar not working - android

In the dropDownButton in the appBar, I want the progress bar to appear when the item is changed. I wrote a code for this, but the progress Bar is not coming.
class arayuzEkrani extends StatefulWidget {
const arayuzEkrani({Key? key}) : super(key: key);
#override
_arayuzEkraniState createState() => _arayuzEkraniState();
}
class _arayuzEkraniState extends State<arayuzEkrani> {
Map<String, String> countryFlags = {
"usa": "🇺🇸 İngilizce",
"almanca": "🇩🇪 Almanca",
};
List <subjectInfo> subjects = [subjectInfo("Selamlaşma", "assets/selamlasma.png"), subjectInfo("Hayvanlar", "assets/hayvanlar.png"), subjectInfo("Teknoloji", "assets/teknoloji.png"), subjectInfo("Meyve Sebze", "assets/meyvesebze.png"), subjectInfo("Meslekler", "assets/meslekler.png")];
// "Selamlaşma", "Hayvanlar", "Teknoloji", "Meyve ve Sebze", "Meslekler"
var defaultFlag = "🇺🇸 İngilizce";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
leading: const Icon(
Icons.public,
color: Colors.black,
size: 27,
),
title: const Text(
"Kelime Öğren",
style: TextStyle(color: Colors.black),
),
elevation: 0,
actions: [
DropdownButton<String>(
items: countryFlags
.map((country, flag) {
return MapEntry(
country,
DropdownMenuItem<String>(
value: flag,
child: Text(flag, style: TextStyle(fontSize: 20),),
));
})
.values
.toList(),
value: defaultFlag,
onChanged: (String? country) {
setState(() {
defaultFlag = country!;
});
},
)
],
),
I want the progress bar with the changed option in the dropDownButton to come. What is the problem? How can I do it?

Try this,
bool isCountryChanged = false; // for update UI when country changed.
appBar: AppBar(
backgroundColor: Colors.transparent,
leading: const Icon(
Icons.public,
color: Colors.black,
size: 27,
),
title: const Text(
"Kelime Öğren",
style: TextStyle(color: Colors.black),
),
elevation: 0,
actions: [
DropdownButton<String>(
items: countryFlags
.map((country, flag) {
return MapEntry(
country,
DropdownMenuItem<String>(
value: flag,
child: Text(flag, style: TextStyle(fontSize: 20),),
));
})
.values
.toList(),
value: defaultFlag,
onChanged: (String? country) {
setState(() {
defaultFlag = country!;
isCountryChanged = true; });
// display circular indicator for 1 second.
Future.delayed(Duration(milliseconds: 1000), () async {
setState(() {
isCountryChanged = false; });
});
},
)
],
),
body:isCountryChanged? CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
strokeWidth: 5,
); : // do stuff

Need a ternary operator which can control your data is ready or not if not CircularProgressIndicator() will shown at appbar unless you can see your data.For example you can be try to fetch data from db that can be take time or maybe you cant fetch data so in this stuation must be show a progress indicator when data ready progress indicator bar will execute.
String? foo;
Future fetchDummy()async{
foo = await dummyData
}
foo !=nul? Text("$foo") : CircularProgressIndicator()

You can create a variable of type duration and then create a function which returns a future builder which is going to await for the duration to finish then display the country but in the mean time will display a circular indicator

Related

Flutter | Have a modalbottomsheet and wish to extract it as a widget

I am implementing a sort by function which displays sort options through a modal bottom sheet, I am able to do it in my "Home Page" widget. Would like to check if I can extract these codes and sub it as a widget for better organization. I am unable to do as I am concerned with the return values from the radio value.
Appreciate any help given, thanks!!
Here is my code:
child: TextButton.icon( // Button to press sort
onPressed: (() {
showModalBottomSheet( // show modal
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10.0)),
context: context,
builder: (BuildContext build) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[ // radio values
RadioListTile(
value: 1,
groupValue: selectedRadioTile,
title: Text(
"Case Earliest to Latest"),
onChanged: (val) {
print(
"Radio Tile pressed $val");
setSelectedRadioTile(val!);
print(selectedRadioTile);
Navigator.pop(context);
},
activeColor:
constants.secondaryBlueColour,
),
RadioListTile(
value: 2,
groupValue: selectedRadioTile,
title: Text(
"Case Latest to Earliest "),
onChanged: (val) {
print(
"Radio Tile pressed $val");
setSelectedRadioTile(val!);
print(selectedRadioTile);
Navigator.pop(context);
},
activeColor:
constants.secondaryBlueColour,
)
],
);
});
}),
icon: Icon(
Icons.sort,
size: 28,
color: constants.textGrayColour,
),
label: Text("Sort",
style: TextStyle(
color: constants.textGrayColour,
fontWeight: FontWeight.bold)))),***
Container(
margin: const EdgeInsets.only(top: 5),
width: MediaQuery.of(context).size.width * 0.5,
decoration: BoxDecoration(
border: Border(
left: BorderSide(
width: 2.0,
color:
constants.categoryButtonBackgroundColour),
bottom: BorderSide(
width: 2.0,
color:
constants.categoryButtonBackgroundColour),
)),
child: TextButton.icon(
onPressed: () {},
icon: Icon(Icons.filter_alt,
size: 28, color: constants.textGrayColour),
label: Text("Filter",
style: TextStyle(
color: constants.textGrayColour,
fontWeight: FontWeight.bold))),
),
],
),
I implemented a SortWidget() but am wondering how I can return the current radio value to my homepage and set the state in the homepage based on the radio value
showModalBottomSheet is a future method, you can use async method for this. and Navigator.pop(context, value); will give you the result. you can also used callback method, seems not needed for your case.
onPressed:()async {
final value = await showModalBottomSheet(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
context: context,
builder: (BuildContext build) {
return MyBottomSheetWidget(selectedRadioTile: selectedRadioTile);
},
);
print("$value");
}
class MyBottomSheetWidget extends StatelessWidget {
// make it statefulWidget if you want to update dialog ui
const MyBottomSheetWidget({
Key? key,
required this.selectedRadioTile,
}) : super(key: key);
final selectedRadioTile;
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
// radio values
RadioListTile(
value: 1,
groupValue: selectedRadioTile,
title: Text("Case Earliest to Latest"),
onChanged: (val) {
print("Radio Tile pressed $val");
Navigator.pop(context, val);
},
),
RadioListTile(
value: 2,
groupValue: selectedRadioTile,
title: Text("Case Latest to Earliest "),
onChanged: (val) {
print("Radio Tile pressed $val");
// setSelectedRadioTile(val!);
print(selectedRadioTile);
Navigator.pop(context, val);
},
)
],
);
}
}
showModalBottomSheet is actually a function which can't converted to widget without having some other widget in place. What you can do is, create a function which hold code of this showModalBottomSheet and call that function on button click.
But if you want to create a separate widget then you can create the widget from the internal code of the showModalBottomSheet which starts with return Column.
You need to create a widget which can take two properties which are int variable named selected and a Function named setSelected. Then you can call that widget from inside the showModalBottomSheet and pass two props from your page. This selected will be set as selectedRadioTile & setSelected will be set as setSelectedRadioTile.
Example Code
class BottomFilter extends StatelessWidget {
const BottomFilter(
{Key? key,
required this.selected,
required this.setSelected})
: super(key: key);
final int selected;
final Function setSelected;
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
// radio values
RadioListTile(
value: 1,
groupValue: selected,
title: Text("Case Earliest to Latest"),
onChanged: (val) {
print("Radio Tile pressed $val");
setSelected(val!);
print(selected);
Navigator.pop(context);
},
activeColor: Colors.amber,
),
RadioListTile(
value: 2,
groupValue: selected,
title: Text("Case Latest to Earliest "),
onChanged: (val) {
print("Radio Tile pressed $val");
setSelected(val!);
print(selected);
Navigator.pop(context);
},
activeColor: Colors.amber,
)
],
);
}
}
Call it like this
builder: (BuildContext build) {
return BottomFilter(selected: selectedRadioTile, setSelected: setSelectedRadioTile);
})
Dartpad link to test this code https://dartpad.dev/?id=9359bc416ae48b996085d6f98a977e27

Why is my CircularProgressIndicator spinning randomly when the screen loads?

I have a CircularProgressIndicator that is meant to display progress when the user presses a button. However, when I open the screen that uses this class it shows a loading animation and I'm not sure why. When the screen opens, it shouldn't show the animation until the user presses the button.
When the screen opens you see something similar to the animation on this page: https://www.c-sharpcorner.com/article/create-chrome-like-loading-animation/
class _MsgInputState extends State<MsgInput> with TickerProviderStateMixin {
AnimationController? controller;
#override
void initState() {
super.initState();
initialize();
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
void initialize() async {
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
)..addListener(() {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Align(
alignment: Alignment.bottomLeft,
child: Column(
children: [
Row(
children: [
GestureDetector(
onTap: () async {
controller?.forward();
},
onLongPressDown: (details) async {
controller?.forward();
},
onLongPressUp: () async {
controller?.stop();
controller?.reset();
},
child: Text('Record'),
),
],
),
Row(
children: <Widget>[
SizedBox(width: 100),
CircularProgressIndicator(
value: controller?.value,
semanticsLabel: 'Linear progress indicator',
),
],
),
],
),
)
);
}
If the value provided to a CircularProgressIndicator is null it just defaults to a spinnging animation.
In the beginnin controller?.value probably returns null. Change it to value: controller?.value ?? 0 so that while it's null 0 is used as a value.
EDIT:
change
CircularProgressIndicator(
value: controller?.value,
semanticsLabel: 'Linear progress indicator',
),
to
CircularProgressIndicator(
value: controller?.value ?? 0,
semanticsLabel: 'Linear progress indicator',
),
Here is a quick examples on how the provided example could be achieved:
class StateProgress extends StatelessWidget {
const StateProgress({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const Center(
child: SizedBox(
height: 50,
width: 50,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.blueAccent /*Should be the color of the indicator*/),
backgroundColor: Colors.white, // Could be made to match the color of the background the indicator is on
),
),
);
}
}

App crashes when DropdownButton is clicked (Flutter)

So I created a DropdownButton in my app. The thing is that whenever I click the dropdown, the app crashes. I'm so confused because when I click other widgets like TextFormFields before clicking the DropdownButton it seems to work properly.
Error Message:
'package:flutter/src/material/dropdown.dart': Failed assertion: line 581 pos 12: 'menuHeight == menuBottom - menuTop': is not true.
Here's my DropdownButton:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DropDownTry(),
);
}
}
class DropDownTry extends StatefulWidget {
const DropDownTry({Key? key}) : super(key: key);
#override
_DropDownTryState createState() => _DropDownTryState();
}
class _DropDownTryState extends State<DropDownTry> {
String dropdownValue = 'Male';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
underline: SizedBox(),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['Male', 'Female']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)),
),
);
}
}
Try below code hope its help to you try to remove const keyword for SizedBox Widget
Declare one String variable for default dropdown value
String? dropdownValue;
Your Dropdown Lists
List gender = [
'Male',
'Female',
'Other',
];
Your Dropdown Widget
DropdownButtonHideUnderline(
child: DropdownButton(
hint: Text(
'Select Gender',
style: TextStyle(
color: Colors.black,
fontSize: 15,
),
textAlign: TextAlign.center,
),
value: dropdownValue,
onChanged: (String? genderNewValue) {
setState(
() {
dropdownValue = genderNewValue;
},
);
},
items: gender.map<DropdownMenuItem<String>>(
(value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(
fontSize: 15,
),
),
);
},
).toList(),
),
),
Your result screen:
I got same error. after struggling 2 days, I figured it out that the problem is about two factors. one is I used dropdown in showModalBottomSheet and second one is I didn't use appBar in scaffold where mydropdown located in. When i located my scaffold that contains my dropdown in, to another screen and add appBar. it worked perfectly.
Wrap your dropdown code in SingleChildScrollView.
ex.
return Scaffold(
body: SingleChildScrollView(
child:Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
underline: SizedBox(),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['Male', 'Female']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)),
),
)
)
Mainly, don't make the DropDown very sticky to the top. It likes some space above.
Also, this happens due to the bad layout of the parent widgets.
Maybe u have made a column with a single child and this child is a stack and the crashed widget is inside the stack.
Try to make a clearer layout of the parent widgets.
also, put the main parent of the screen in a Material Widget.
The problem is caused because the framework can't calculate the heights beyond the menu.

How can I prevent Navigator push from refreshing the screen

I am writing a flutter program where the user should select a value from a DropdownButtonFormField. once the selection is made, the choice should be displayed on the dropdown. I use a push route to get the data from a second screen in which the choice is utilized. My problem is after selecting the option, the page refreshes and therefore doesnt show the selected value on the dropdown.
Below is my code:
I create the Dropdownbuttonformfield in a file called shared.dart so I can call it in multiple files:
class UserDropdownList extends StatefulWidget {
#override
_UserDropdownListState createState() => _UserDropdownListState();
}
class _UserDropdownListState extends State<UserDropdownList> {
String currentUser;
#override
Widget build(BuildContext context) {
final user = Provider.of<List<User>>(context) ?? [];
return DropdownButtonFormField(
isExpanded: true,
decoration: textInputDecoration,
value: currentUser,
hint: Text(
'Incoming Officer',
),
onChanged: (val) {
setState(() => currentUser = val);
var route = MaterialPageRoute(
builder: (BuildContext context) =>
FinalForm(chosenUser: currentUser,)
);
Navigator.of(context).push(route);
},
// onChanged: (val) => setState(() => currentUser = val),
items: user.map((user){
return DropdownMenuItem(
value: user.userId,
child: Text(user.name)
);
}).toList(),
);
}
}
I then call the Custom button in my main page like so
class FinalForm extends StatefulWidget {
//code for importing selected user
final String chosenUser;
FinalForm({Key key, this.chosenUser}) : super (key: key);
#override
_FinalForm createState() => _FinalFormState();
}
class _FinalFormState extends State<FinalForm> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Final Form')
),
body: Form(
child: Center(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.fromLTRB(5, 5, 5, 5),
children: <Widget>[
SizedBox(height: 20.0),
Align(
child: Text(
'Select Incoming Officer',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.blueAccent,
),
)
),
SizedBox(height: 20.0),
StreamProvider<List<User>>.value(
value: DatabaseService().users,
child: UserDropdownList(),
),
SizedBox(height: 20.0),
Text("${widget.chosenUser}"),
],),
),
),
);
}
}
Is there a way to keep the selected value on the dropdown or prevent the screen from reloading?
If you are navigating away from the current page / view, it would make sense for the current dropdown selection to be lost. You can pass the current selection as an argument to the push function to redisplay on the new page. Hth

Flutter Switching FAB on PageView Pages with animation in Scaffold

So I've looked several places for how this is supposed to be implemented and I'm sure I'm missing something trivial, but I have a flutter app with a Scaffold who's body is a PageView. I need to have a different FAB for some pages and the way it's currently set up is the floatingActionButton attribute of the scaffold is set to access an array of FloatingActionButtons with the index being the _currentPageIndex (private variable shared by bottomNavBar and _pageController.
This changes the FAB abruptly which is not the desired behavior.
I'm trying to get the FAB to animate (scale out and scale back) in when the page changes like in the material spec:
Tabbed Screens
When tabs are present, the FAB should briefly disappear, then >reappear when the new content moves into place. This expresses >that the FAB is not connected to any particular tab.
I would appreciate any advice on how to go about implementing it simply (I'm pretty sure I'm missing something trivial). The alternative is to manually animate in and out FABs myself by wrapping it in something.
You could try using AnimatedCrossFade Widget something like this :
class TestingNewWidgetState extends State<TestingNewWidget> {
int currentIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: AnimatedCrossFade(
crossFadeState: currentIndex == 0
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
duration: Duration(seconds: 1),
firstChild: FloatingActionButton(
onPressed: () => null,
child: Icon(Icons.arrow_left),
backgroundColor: Colors.red,
),
secondChild: FloatingActionButton(
onPressed: () => null,
child: Icon(Icons.arrow_right),
backgroundColor: Colors.blue,
),
),
body: PageView(
onPageChanged: (index) {
setState(() {
currentIndex = index;
});
},
children: <Widget>[
Scaffold(
body: Center(
child: Text("page 1"),
),
),
Scaffold(
body: Center(
child: Text("page 2"),
),
),
],
),
);
}
}
UPDATE
Remember you can create your own widget, this is an example using a custom FloatingActionButton:
class TestingNewWidgetState extends State<TestingNewWidget> {
int currentIndex = 0;
#override
Widget build(BuildContext context) {
var customFabButton;
if (currentIndex == 0) {
customFabButton = CustomFabButton(
color: Colors.red,
onPressed: () => null,
icon: Icons.alarm,
);
} else if (currentIndex == 1) {
customFabButton = CustomFabButton(
color: Colors.blue,
onPressed: () => null,
icon: Icons.satellite,
);
} else {
customFabButton = CustomFabButton(
color: Colors.green,
onPressed: () => null,
icon: Icons.verified_user,
);
}
return Scaffold(
floatingActionButton: customFabButton,
body: PageView(
onPageChanged: (index) {
setState(() {
currentIndex = index;
});
},
children: <Widget>[
Scaffold(
body: Center(
child: Text("page 1"),
),
),
Scaffold(
body: Center(
child: Text("page 2"),
),
),
Scaffold(
body: Center(
child: Text("page 3"),
),
),
],
),
);
}
}
class CustomFabButton extends StatelessWidget {
final IconData icon;
final Color color;
final VoidCallback onPressed;
const CustomFabButton({Key key, this.icon, this.color, this.onPressed})
: super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: AnimatedContainer(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: color,
),
duration: Duration(seconds: 1),
height: 50.0,
width: 50.0,
child: Icon(icon),
),
);
}
}

Categories

Resources