Way to change floating button icon when pressed - dart

Is there any way to change the floating button's icon when pressed?
Code:
var musicButton = new FloatingActionButton(
onPressed: (){
if(playerState == PlayerState.playing) {
//setState(() => child = new Icon(Icons.pause));
setState((){
child: new Icon(Icons.play_arrow);
});
pause();
} else if (playerState == PlayerState.paused){
setState((){
child: new Icon(Icons.pause);
});
play();
}
},
tooltip: 'Play Music',
child: new Icon(Icons.pause));

You can just use a state variable to switch between icons.
Example:
bool playing = false;
then
var musicButton = new FloatingActionButton(
onPressed: (){
if(playerState == PlayerState.playing) {
setState((){
playing = false;
});
pause();
} else if (playerState == PlayerState.paused){
setState((){
playing = true;
});
play();
}
},
tooltip: 'Play Music',
child: playing?new Icon(Icons.pause):new Icon(Icons.play_arrow));
Hope that helped!

You can use enumeration also enum used to define named constant values.
enum Gender {Male,Female,} // enum definition globally
Gender selectedGender; // enum declaration
onPress: () {
setState(() {
selectedGender = Gender.Male;
});
},
Now you can use selectedGender Dart Conditional Operators ( ? : ) anywhere in your code.

Related

Flutter bloc yield state is not received in listener

I add a event to bloc immediately after pop a CupertinoModalPopup. But yield a state and not received in bloc listener. The code is like below.There are more states and events, and it's all working, except this state is not received. I don't know why, maybe it's because I add the event after the showCupertinoModalPopup was pop. I am not sure. I didn't paste the whole codes here, I only kept the relevant code. I did set the breakpoint on yield ProfileUploadAvatarSucceed(user), and it stops here.
BlocBuilder<ProfileBloc, ProfileState>(
builder: (context, state) {
return GestureDetector(
onTap: () async {
final file = await showCupertinoModalPopup(
context: context,
builder: (popupContent) {
return CupertinoActionSheet(
actions: [
CupertinoActionSheetAction(
child: Text('Photo Library'),
onPressed: () async {
final imageFile = await _picker.getImage(
source: ImageSource.gallery,
imageQuality: 70,
maxWidth: 480);
if (imageFile != null) {
Navigator.of(context).pop(imageFile);
}
},
),
],
);
});
BlocProvider.of<ProfileBloc>(context).add(UploadAvatarEvent(file));
};
},
listener: (_, state) {
if (state is ProfileLoadingState) {
BotToast.showLoading();
} else if (state is ProfileUploadAvatarSucceed) {
BotToast.closeAllLoading();
BotToast.showText(text: 'Avatar updated');
}
}
);
ProfileBloc.dart
#override
Stream<ProfileState> mapEventToState(
ProfileEvent event,
) async* {
if (event is UploadAvatarEvent) {
yield ProfileLoadingState();
await _apiProvider.updateUser(currentUser.id, payload);
final user = await _apiProvider.getUser(currentUser.id);
yield ProfileUploadAvatarSucceed(user);
}
}

Google Maps Flutter Custom marker not showing

Because I had problems with Google Maps and Bitmap descriptor, I tried to preload the icons before loading the Map, like this:
Widget preloadPinsAndLoadSearchMap() {
final config = ImageConfiguration(size: Size(48, 48));
final activePin = 'assets/active-pin.png';
final neutralPin = 'assets/neutral-pin.png';
final home = 'assets/home-map.png';
return FutureBuilder(
future: Future.wait([
BitmapDescriptor.fromAssetImage(config, activePin),
BitmapDescriptor.fromAssetImage(config, neutralPin),
BitmapDescriptor.fromAssetImage(config, home),
]),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return SearchMap(pins: snapshot.data);
} else {
return Container();
}
},
);
}
And the SearchMap and the Markers:
// Each Marker is produced like this:
Marker createMarker(dynamic seller) {
return Marker(
markerId: MarkerId(seller.id),
position: LatLng(seller.latitude, seller.longitude),
onTap: () => onMarkerTap(seller),
// The icons are coming from the FutureBuilder.
icon: isActive ? activePin : neutralPin,
);
}
class SearchMap extends StatelessWidget {
Widget build(BuildContext context) {
final paris = initialPosition ??
LatLng(parisPosition.latitude, parisPosition.longitude);
return GoogleMap(
myLocationButtonEnabled: false,
zoomControlsEnabled: false,
compassEnabled: false,
mapType: MapType.normal,
markers: markers,
initialCameraPosition: CameraPosition(target: paris, zoom: 15),
onMapCreated: onMapCreated,
onTap: (obj) => print(obj),
);
}
}
Happily it works on my devices very well, both iOS and Android. But on my reviewers devices, the icons are just not showing up. Is it a correct way to handle BitmapDescriptor? Maybe I’m wrong on the way to do this?

How to make sure that async function is loaded before Widget build?

I have a screen with some informations about the user.
In my build widget I have this:
Column
(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>
[
Text('User Points', style: TextStyle(color: Colors.blueAccent)),
Text('${this.user.points}', style: TextStyle(color: Colors.black, fontWeight: FontWeight.w700, fontSize: 34.0))
],
),
And I have a function to update the dashboard each time user access it.
void updateDashboard() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
var api_key = prefs.getString('api_key');
var userID = prefs.getInt('userID');
var res = await http.post(('http://myApp.com/getDashboardPreferences/'+userID.toString() + '/' + api_key),
headers: {
"Accept": "application/json",
"content-type": "application/json"
});
this.user = User.fromJson(json.decode(res.body));
setState(() {
if (res.statusCode == 200) {
this.user.setPoints(this.user.points);
} else {
print("Something is wrong");
}
});
}
And in initstate I call that function:
#override
void initState() {
updateDashboard();
super.initState();
}
This works, but however, I get that ${this.user.points} is null for a few seconds (I get that warning screen) and then it is loaded and everything is fine. I guess it is because my function is async... But how can I assure that this updateDashboard() is called before rendering Widget build() ?
If you don't want to use FutureBuilder then you can do something like
if(done)
Column( /* whatever */ )
else
Center(child: CircularProgressIndicator())
void updateDashboard() async{
/* whatever */
if (res.statusCode == 200) {
setState(() {
this.user.setPoints(this.user.points);
done = true;
});
}
#override
void initState() {
/* whatever */
done = false;
}
Well, async means that the data might take time to load and is handed off to another thread to load. There can be multiple non blocking ways to do this:
Provide a default value for the user object, so by the time the async method finishes, you show proxy/default values and setState() makes sure the new value gets displayed when loaded.
This method requires you to load widgets conditionally, i.e load Text widget when there's data else load a Container().
Example for the second approach:
this.user!=null?Text('${this.user.points}'):Container()
This way you don't need to show the widget till the value loads. Either of the approaches work and depending on your use-case, you can choose which you can apply.
You can put the function right before the return widget.
You can make a loading variable like bool _loading then anywhere you start loading you async function you can setState of loading to true then setState of loading to false when async function stops loading like this.
void updateDashboard() async{
setState(() {
_loading = true;
});
SharedPreferences prefs = await SharedPreferences.getInstance();
var api_key = prefs.getString('api_key');
var userID = prefs.getInt('userID');
var res = await http.post(('http://myApp.com/getDashboardPreferences/'+userID.toString() + '/' + api_key),
headers: {
"Accept": "application/json",
"content-type": "application/json"
});
this.user = User.fromJson(json.decode(res.body));
setState(() {
if (res.statusCode == 200) {
this.user.setPoints(this.user.points);
} else {
print("Something is wrong");
}
_loading = false; //Set Loading to false
});
}
Then in your widget build, check for when loading is true and show a progress indicator like this.
body: _loading
? Center(
child: CircularProgressIndicator( //Show a Circular Progress indicator
valueColor: new AlwaysStoppedAnimation<Color>(Colors.cyan),
),
)
:Column(//Show your Dashboard)
I hope it helps

flutter_search_bar plugin onClose listener

I am currently using flutter_search_bar plugin with a Tabbed Pages. Since my values change dynamically. I was wondering if there were any ways to add a listener to the package in order to make the necessary changes.
The plugin is here:
https://pub.dartlang.org/flutter/packages?q=flutter_search_bar&api=0
Update: this is what my code looks like now
_NavigationState() {
searchBar = new SearchBar(
controller: controller,
inBar: false,
setState: setState,
onSubmitted: onSubmit,
onChanged: onChange,
buildDefaultAppBar: buildAppBar,
hintText: 'Search Book',
closeOnSubmit: false,
clearOnSubmit: true,
onClosed: onClosed,
);
}
void onClosed() {
isSeaching = false;
loadTabpages();
setState(() {});
}
You can fork the repo and make the changes :
https://github.com/ArcticZeroo/flutter-search-bar/blob/master/lib/src/flutter_search_bar_base.dart
Add this var in SearchBar class:
/// Event triggered when the user close the search bar
final VoidCallback onClosed;
Add the field into the constructor
SearchBar(
{#required this.setState,
#required this.buildDefaultAppBar,
this.onSubmitted,
this.controller,
this.hintText = 'Search',
this.inBar = true,
this.colorBackButton = true,
this.closeOnSubmit = true,
this.clearOnSubmit = true,
this.showClearButton = true,
this.onChanged,
this.onClosed})
Line 144
Set a widget into the leading property
new IconButton(
icon: const BackButtonIcon(),
color: buttonColor,
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
onPressed: () {
if (onClosed != null) {
onClosed();
}
Navigator.maybePop(context);
}),
Using the listener:
new SearchBar(
inBar: true,
setState: setState,
showClearButton: false,
clearOnSubmit: false,
closeOnSubmit: false,
onClosed: () {
}), ...
Updated handling the back button
return new AppBar(
leading: WillPopScope(
onWillPop: () {
if (onClosed != null) {
onClosed();
}
return Future.value(true);
},
child: new IconButton(
icon: const BackButtonIcon(),
color: buttonColor,
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
onPressed: () {
if (onClosed != null) {
onClosed();
}
controller.clear();
Navigator.maybePop(context);
}),
),
....

Programmatically focusing on InputField and opening up keyboard

I have a situation where I need to programmatically focus on a InputField(such as in response to a button press).
I'm using the Focus.moveTo function; however, even though the InputField is focused (blinking cursor appears) the keyboard is not brought up.
It seems like the best solution would be to call the RequestKeyboard() function in _InputFieldState, but this is private.
What would be the best way to accomplish this?
Here is a code sample showing the workflow:
class InputFieldWrapper extends StatefulWidget {
#override
_InputFieldWrapperState createState() => new _InputFieldWrapperState();
}
class _InputFieldWrapperState extends State<InputFieldWrapper> {
InputValue _currentInput = new InputValue(text: 'hello');
// GlobalKey for the InputField so we can focus on it
GlobalKey<EditableTextState> _inputKey = new GlobalKey<EditableTextState>();
#override
Widget build(BuildContext context) {
return new Column(
children: [
// Button that should focus on the InputField when pressed
new IconButton(
icon: new Icon(Icons.message),
onPressed: () {
Focus.moveTo(_inputKey);
},
),
// InputField that should be focused when pressing the Button
new InputField(
value: _currentInput,
key: _inputKey,
onChanged: (InputValue input) {
setState(() {
_currentInput = input;
});
}
),
],
);
}
}
This was decided to be a bug and is being tracked at #7985.

Resources