i am fairly new in flutter and i am trying to make a music player app.
the app works fine and all but the problem is it doesnot play in background/lockscreen.
i went through some docs and it says to use audio_service package for that but i am currently using flutter_audio_query package...all the docs i went through shows a solution which results in entirely changing the code...
so my question is ...is there any way to make the app play in background without changing the code entirely?
heres what my code looks like
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_audio_query/flutter_audio_query.dart';
import 'package:just_audio/just_audio.dart';
class MusicPlayer extends StatefulWidget {
SongInfo songInfo;
Function changeTrack;
final GlobalKey<MusicPlayerState> key;
MusicPlayer({required this.songInfo, required this.changeTrack, required this.key}):super(key: key);
#override
MusicPlayerState createState() => MusicPlayerState();
}
class MusicPlayerState extends State<MusicPlayer> {
double minimumValue = 0.0, maximumValue = 0.0, currentValue = 0.0;
String currentTime = '', endTime = '';
bool isPlaying = false;
final AudioPlayer player = AudioPlayer();
void initState() {
super.initState();
setSong(widget.songInfo);
}
void dispose(){
super.dispose();
player?.dispose();
}
void setSong(SongInfo songInfo) async {
widget.songInfo = songInfo;
await player.setUrl(widget.songInfo.uri);
currentValue = minimumValue;
maximumValue = player.duration!.inMilliseconds.toDouble();
setState(() {
currentTime = getDuration(currentValue);
endTime = getDuration(maximumValue);
});
isPlaying=false;
changeStatus();
player.positionStream.listen((duration) {
currentValue=duration.inMilliseconds.toDouble();
setState((){
currentTime=getDuration(currentValue);
});
});
}
void changeStatus(){
setState((){
isPlaying=!isPlaying;
});
if(isPlaying){
player.play();
}else{
player.pause();
}
}
String getDuration(double value) {
Duration duration = Duration(milliseconds: value.round());
return [duration.inMinutes, duration.inSeconds]
.map((e) => e.remainder(60).toString().padLeft(2, '0'))
.join(':');
}
Widget build(context) {
return Scaffold(
backgroundColor: Colors.black38,
appBar: AppBar(
backgroundColor: Colors.black,
leading: IconButton(
onPressed: () {
Navigator.of(context).pop();
},
icon: Icon(
Icons.arrow_back,
color: Colors.white,
),
),
title: const Text(
'Now Playing',
style: TextStyle(color: Colors.white),
),
),
body: Container(
margin: EdgeInsets.fromLTRB(15, 50, 5, 0),
child: Column(
children: <Widget>[
CircleAvatar(
backgroundImage: widget.songInfo.albumArtwork == null
? AssetImage('assets/images/album_image.jpg')
: FileImage(
File(widget.songInfo.albumArtwork),
) as ImageProvider,
radius: 95,
),
Container(
color: Colors.black,
margin: EdgeInsets.fromLTRB(0, 10, 0, 7),
child: Text(
widget.songInfo.title,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600),
),
),
Container(
color: Colors.black,
margin: EdgeInsets.fromLTRB(0, 0, 0, 15),
child: Text(
widget.songInfo.artist,
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500),
),
),
Slider(
value: currentValue,
min: minimumValue,
max: maximumValue,
onChanged: (value) {
currentValue = value;
player.seek(Duration(milliseconds: currentValue.round()));
},
inactiveColor: Colors.grey,
activeColor: Colors.green,
),
Container(
transform: Matrix4.translationValues(0, -5, 0),
margin: EdgeInsets.fromLTRB(5, 0, 5, 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
currentTime,
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500),
),
Text(
endTime,
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500),
),
],
),
),
Container(
margin: EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
child: Icon(Icons.skip_previous,
color: Colors.white, size: 55,),
behavior: HitTestBehavior.translucent,
onTap: () {
widget.changeTrack(false);
},
),
GestureDetector(
child: Icon(isPlaying?Icons.pause:Icons.play_arrow,
color: Colors.white, size: 75,),
behavior: HitTestBehavior.translucent,
onTap: () {
changeStatus();
},
),
GestureDetector(
child: Icon(Icons.skip_next,
color: Colors.white, size: 55,),
behavior: HitTestBehavior.translucent,
onTap: () {
widget.changeTrack(true);
},
),
],
),
),
],
),
),
);
}
}
appreciate any help that i can get...thank you
The description of "flutter_audio_query" says clearly that it is no audio player. It is a library to fetch music albums and covers.
"audio_service" on the other hand says clearly that it is for playing audio.
I would suggest to start again if you won't cause problems which could be avoided
Use the dependency assets_audio_player. it has a built-in function for this no need to even code. there is a little one-screen player for you. Just copy paste the code below and must add all the dependencies. Music will be played in background, on notification bar and on lock screen.
import 'dart:convert';
import 'package:assets_audio_player/assets_audio_player.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:musiccontrol/MusicPlayerModel.dart';
class Home extends StatefulWidget {
Home({
super.key,
t,
});
MusicPlaylist? _musicPlaylist;
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
List SongsList = [];
bool MuteVolume = false;
// Fetch content from the json file
final AssetsAudioPlayer audioplayer = AssetsAudioPlayer();
double screenwidth = 0;
double screenheight = 0;
#override
void initState() {
super.initState();
setupPlaylist();
}
// songs paths and links
void setupPlaylist() async {
await audioplayer.open(
showNotification: true,
Playlist(audios: [
// Audio(SongsList[2].,
// metas: Metas(
// title: "Bazz01",
// artist: 'Talha',
// )),
Audio.network(
'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/09/17/bb/0917bbe1-58c3-6252-d00e-9b70d42ef5dc/mzaf_2269500085377778268.plus.aac.p.m4a',
metas: Metas(
id: 'Online',
title: 'Online',
artist: 'Florent Champigny',
album: 'OnlineAlbum',
// image: MetasImage.network('https://www.google.com')
image: const MetasImage.network(
'https://i.dawn.com/large/2021/09/61399fb500900.png'),
),
),
Audio.network(
'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/09/17/bb/0917bbe1-58c3-6252-d00e-9b70d42ef5dc/mzaf_2269500085377778268.plus.aac.p.m4a',
metas: Metas(
id: 'Online',
title: 'Online',
artist: 'Florent Champigny',
album: 'OnlineAlbum',
// image: MetasImage.network('https://www.google.com')
image: const MetasImage.network(
'https://i.dawn.com/large/2021/09/61399fb500900.png'),
),
),
]),
autoStart: true,
loopMode: LoopMode.playlist,
);
}
var forward= AssetImage('assets/forward.png');
#override
void dispose() {
super.dispose();
setupPlaylist();
}
Widget slider(RealtimePlayingInfos realtimePlayingInfos) {
return SliderTheme(
data: const SliderThemeData(
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 8),
),
child: Slider.adaptive(
activeColor: const Color.fromARGB(255, 241, 241, 241),
inactiveColor: const Color.fromARGB(255, 219, 217, 217),
thumbColor: const Color.fromARGB(255, 255, 255, 255),
value: realtimePlayingInfos.currentPosition.inSeconds.toDouble(),
max: realtimePlayingInfos.duration.inSeconds.toDouble(),
onChanged: (value) {
audioplayer.seek(Duration(seconds: value.toInt()));
}));
}
Widget timeStamps(RealtimePlayingInfos realtimePlayingInfos) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
transformString(realtimePlayingInfos.currentPosition.inSeconds),
style: const TextStyle(color: Colors.white),
),
Text(transformString(realtimePlayingInfos.duration.inSeconds),
style: const TextStyle(color: Colors.white)),
],
);
}
// slider timings
String transformString(int seconds) {
String minuteString =
'${(seconds / 60).floor() < 10 ? 0 : ''}${(seconds / 60).floor()}';
String secondString = '${seconds % 60 < 10 ? 0 : ' '}${seconds % 60}';
return '$minuteString:$secondString';
}
//control buttons of music player
Widget playBar(RealtimePlayingInfos realtimePlayingInfos) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon_Button(
icon: Icons.compare_arrows_rounded,
size: 21,
passedfunc: () {
audioplayer.toggleShuffle();
}),
Icon_Button(
icon: Icons.fast_rewind_rounded,
size: 21,
passedfunc: () {
audioplayer.seekBy(const Duration(seconds: -15));
}),
Icon_Button(
icon: Icons.skip_previous_rounded,
size: 21,
passedfunc: () => audioplayer.previous()),
Icon_Button(
icon: realtimePlayingInfos.isPlaying
? Icons.pause_circle_filled_rounded
: Icons.play_circle_fill_rounded,
size: 50,
passedfunc: () => audioplayer.playOrPause()),
Icon_Button(
icon: Icons.skip_next_rounded,
size: 21,
passedfunc: () => audioplayer.next()),
Icon_Button(
icon: Icons.fast_forward_rounded,
size: 21,
passedfunc: () {
setState(() {
audioplayer.seekBy(const Duration(seconds: 15));
});
}),
Icon_Button(
icon: MuteVolume == true
? Icons.volume_off_outlined
: Icons.volume_up,
size: 21,
passedfunc: () {
if (MuteVolume == true) {
setState(() {
audioplayer.setVolume(1);
MuteVolume = !MuteVolume;
});
} else {
audioplayer.setVolume(0);
MuteVolume = !MuteVolume;
}
},
),
],
),
);
}
#override
Widget build(BuildContext context) {
// screenheight = MediaQuery.of(context).size.height;
// screenwidth = MediaQuery.of(context).size.width;
return Scaffold(
backgroundColor: const Color.fromARGB(31, 142, 111, 253),
body: audioplayer.builderRealtimePlayingInfos(
builder: (context, realtimePlayingInfos) {
// ignore: unnecessary_null_comparison
if (realtimePlayingInfos != null) {
return Column(
//Designing Texts and image
children: [
Padding(
padding: const EdgeInsets.all(23.0),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(40),
child: Image.network(
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTUyCn1ItXdchQzeH8MkPEtQJcKttTplAw7oDrBuQI&s',
height: 70,
width: 70,
)),
Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Sound Cloud',
style: TextStyle(color: Colors.white, fontSize: 18),
),
const Text(
'Name Goes',
style: TextStyle(color: Colors.white, fontSize: 15),
),
const Text(
'Song Name Goes here',
style: TextStyle(color: Colors.white, fontSize: 20),
),
],
),
),
],
),
),
// slider code is here
Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
slider(realtimePlayingInfos),
SizedBox(
height: screenheight * 0.05,
),
timeStamps(realtimePlayingInfos),
SizedBox(
height: screenheight * 0.05,
),
playBar(realtimePlayingInfos),
],
)
],
),
],
);
} else {
return Column();
}
}),
);
}
}
// Icon_Button custom widget
// ignore: camel_case_types, must_be_immutable
class Icon_Button extends StatelessWidget {
Icon_Button(
{super.key,
required this.icon,
required this.size,
required this.passedfunc});
IconData icon;
double size;
final passedfunc;
#override
Widget build(BuildContext context) {
return IconButton(
onPressed: passedfunc,
icon: Icon(icon),
iconSize: size,
color: Colors.grey,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
);
}
}
Related
Evertime the setCountDown method is called i get an exception SetStateCalled during build ,,, please help! . I have to display this countdown in a previous screen too , Lets call it screen A and from A i pushReplaced and came to this Charging Screen which displays countdown and uses provider too . But everytime I update the provider value I get an error . It works fine in the debug mode but as soon as build a release it works weirdly . Please help!!!!
import 'dart:async';
import 'dart:developer';
import 'package:chargeapp_master/ChangeNotifiers/ChargeTimeNotifier.dart';
import 'package:chargeapp_master/Screens/main_map_screen.dart';
import 'package:chargeapp_master/Screens/navigation.dart';
import 'package:chargeapp_master/Screens/profile_screens/profile_screen.dart';
import 'package:chargeapp_master/Screens/wallet_screens/wallet_screen.dart';
import 'package:chargeapp_master/assistants/assistant_methods.dart';
import 'package:chargeapp_master/assistants/bluetooth_assistants.dart';
import 'package:chargeapp_master/widgets/bottbar.dart';
import 'package:circular_countdown_timer/circular_countdown_timer.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_fonts/google_fonts.dart';
import '../User Details.dart';
import '../main.dart';
import 'Charging_summary_screen.dart';
import 'package:provider/provider.dart';
class Charging_screen extends StatefulWidget {
int duration;
String amount;
Charging_screen({Key? key, required this.duration, required this.amount})
: super(key: key);
#override
_Charging_screenState createState() => _Charging_screenState();
}
class _Charging_screenState extends State<Charging_screen> with ChangeNotifier {
double h(double height) {
return MediaQuery.of(context).size.height * height;
}
double w(double width) {
return MediaQuery.of(context).size.width * width;
}
void showNotification(String dat) {
flutterLocalNotificationsPlugin.show(
0,
"Powerstrip",
"Charging $dat",
NotificationDetails(
android: AndroidNotificationDetails(channel.id, channel.name,
importance: Importance.high,
color: Colors.blue,
playSound: true,
icon: '#mipmap/ic_launcher')));
}
CountDownController _controller = CountDownController();
Timer? countdownTimer;
Duration myDuration = Duration();
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
Provider.of<ChargeTimeProvider>(context, listen: false)
.setDuration(Duration(minutes: widget.duration));
startTimer();
});
}
void setCountDown() {
final reduceSecondsBy = 1;
var seconds = Provider.of<ChargeTimeProvider>(context, listen: false)
.getDuration()
.inSeconds -
reduceSecondsBy;
log("got seconds from provider " + seconds.toString());
if (seconds < 0) {
countdownTimer?.cancel();
showNotification("Stopped");
log("$charging");
} else {
Provider.of<ChargeTimeProvider>(context, listen: false)
.setDuration(Duration(seconds: seconds));
// myDuration = Duration(seconds: seconds);
}
// setState(() {
// final seconds = myDuration.inSeconds - reduceSecondsBy;
// });
}
void startTimer() {
showNotification("Started");
context.read<ChargeTimeProvider>().setChargeStatus(true);
countdownTimer =
Timer.periodic(Duration(seconds: 1), (_) => setCountDown());
}
void stoptimer() {
Provider.of<ChargeTimeProvider>(context, listen: false)
.setChargeStatus(false);
// context.read<ChargeTimeProvider>().setChargeStatus(false);
log("$charging");
countdownTimer?.cancel();
}
String mins = "";
String sec = "";
var minutesChargedfor;
Future<bool> _onWillPop() async {
return (await showDialog(
barrierDismissible: true,
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.black,
title: Text(
'Are you sure?',
style: GoogleFonts.sulphurPoint(color: Colors.white),
),
content: Text(
'Do you want to go to home screen',
style: GoogleFonts.sulphurPoint(color: Colors.white),
),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext) => Navigation()));
},
child: const Text('Yes'),
),
],
),
)) ??
false;
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _onWillPop(),
child: Scaffold(
backgroundColor: Color(0xff1B1D20),
body: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 70),
Center(
child: Container(
padding: EdgeInsets.all(12),
// margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(7),
),
child: Text(
"Congratulations, you've added Green miles🍀",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.green.shade600),
),
),
),
const SizedBox(
height: 20,
),
Center(
child: Column(
children: [
Center(
child: CircularCountDownTimer(
duration: widget.duration * 60,
controller: _controller,
width: MediaQuery.of(context).size.width / 2,
height: MediaQuery.of(context).size.height / 2,
ringColor: Colors.grey[400]!,
ringGradient: const LinearGradient(
colors: [Colors.white10, Colors.white70]),
fillColor: Colors.blue,
fillGradient: LinearGradient(colors: [
Colors.blue.shade300,
Colors.blue.shade900
]),
backgroundColor: Colors.grey[500],
backgroundGradient: LinearGradient(colors: [
Colors.grey.shade900,
Colors.grey.shade500
]),
strokeWidth: 20.0,
strokeCap: StrokeCap.round,
textStyle: const TextStyle(
fontSize: 33.0,
color: Colors.white,
fontWeight: FontWeight.bold),
// textFormat: CountdownTextFormat.MM_SS,
isTimerTextShown: true,
isReverse: false,
onComplete: () async {
var secondsChargedfor = widget.duration * 60;
var res = AssistantMethods.stopCharging(
secondsChargedfor, "0");
if (res != "failure") {
await Ble.disconnectDevice(connectedDevice);
stoptimer();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Charging_summary(
amount: widget.amount,
duration: widget.duration,
mins: mins,
sec: sec)));
} else {
Fluttertoast.showToast(msg: res);
}
Ble.disconnectDevice(connectedDevice);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => Charging_summary(
amount: widget.amount,
duration: widget.duration,
mins: mins,
sec: sec)));
},
),
),
// SizedBox(height: 10,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.flash_on,
size: 30,
color: Colors.blue,
),
SizedBox(
width: 7,
),
Text(
"Charging",
style: TextStyle(
fontSize: 16,
color: Colors.white,
fontWeight: FontWeight.w400),
)
],
),
BuildTime(),
],
),
),
const SizedBox(
height: 40,
),
Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: Container(
padding: EdgeInsets.all(15),
// height: 130,
// width:MediaQuery.of(context).size.width ,
decoration: const BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.only(
topRight: Radius.circular(10.0),
topLeft: Radius.circular(10.0)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
user_device_id.toString(),
style: const TextStyle(
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.w400),
),
],
),
device_type == 0
? Text(
"₹${widget.amount}",
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18,
color: Colors.white),
)
: const Text(""),
],
),
const SizedBox(
height: 10,
),
Row(
children: [
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
primary: Colors.red.shade900,
padding: const EdgeInsets.symmetric(
horizontal: 50, vertical: 15),
// minimumSize: Size(50,20 ),
),
child: const Text(
"Stop",
style: TextStyle(
fontSize: 15, color: Colors.white),
),
onPressed: () async {
var secondsChargedfor = widget.duration * 60;
secondsChargedfor = secondsChargedfor -
(int.parse(mins) * 60 + int.parse(sec));
log(secondsChargedfor.toString());
var res = AssistantMethods.stopCharging(
secondsChargedfor, "1");
if (res != "failure") {
await Ble.startStopTimer(services,
0.toString()); // to stop the charging
_controller.pause();
await Ble.disconnectDevice(connectedDevice);
stoptimer();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
Charging_summary(
amount: widget.amount,
duration: widget.duration,
mins: mins,
sec: sec)));
} else {
Fluttertoast.showToast(msg: res);
}
},
),
),
],
),
],
),
),
),
],
),
],
),
//bottomNavigationBar: bottbar(),
),
);
}
Widget BuildTime() {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final hours = twoDigits(
context.read<ChargeTimeProvider>().getDuration().inHours.remainder(24));
final minutes = twoDigits(context
.read<ChargeTimeProvider>()
.getDuration()
.inMinutes
.remainder(60));
final seconds = twoDigits(context
.read<ChargeTimeProvider>()
.getDuration()
.inSeconds
.remainder(60));
mins = minutes;
sec = seconds;
Provider.of<ChargeTimeProvider>(context, listen: true)
.setTime("$hours:$minutes:$seconds");
return Consumer<ChargeTimeProvider>(
builder: (context, chargetimenotifier, child) => Text(
"${chargetimenotifier.getTime()} Time Remaining",
style: TextStyle(fontSize: 14, color: Colors.white),
));
}
#override
void dispose() {
super.dispose();
}
}
Add Some Delay
Future.delayed(const Duration(milliseconds: 500), () {
// Here you can write your code
setState(() {
// Here you can write your code for open new view
});
});
function description: when an item is clicked, change the background color of this item 。
I defined the variable color in the List.generate function and used the setState function in onTapDown to modify this value, but it has no effect. what should I do?
my code is as follows
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class Homepage extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Homepage> {
final titles = ["AAA", "BBB", "CCC", "DDD"];
final subtitles = [
"我去,额度这么高?",
"XX,你上次给我兑换的烤箱还不错哦",
"抱歉,我觉得我们不是很合适",
"邻居你好,你家的租户最近有点吵"
];
final date = ["昨天 18:08", "星期二", "7月21日", "7月19日"];
final avatar = [
"WechatIMG325.jpeg",
"WechatIMG326.jpeg",
"WechatIMG327.jpeg",
"WechatIMG328.jpeg"
];
// final icons = [Icons.ac_unit, Icons.access_alarm, Icons.access_time];
#override
Widget build(BuildContext context) {
return Column(
children: [
...List.generate(titles.length, (index) {
var color = Colors.white;
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (TapDownDetails details) {
setState(() {
color = Colors.grey;
});
debugPrint("presed GestureDetector");
},
onTapUp: (TapUpDetails details) {
setState(() {
color = Colors.white;
});
debugPrint("presed GestureDetector");
},
child: Container(
color: color,
margin: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Row(children: [
SizedBox(
width: 50,
child: Container(
margin: const EdgeInsets.fromLTRB(0, 10, 0, 10),
child: ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child: Image.asset("assets/images/${avatar[index]}",
height: 50, width: 50),
),
),
),
//const SizedBox(width: 10),
IntrinsicWidth(
child: Container(
margin: const EdgeInsets.fromLTRB(10, 0, 0, 0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
titles[index],
style: const TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.w300,
),
),
const SizedBox(height: 5),
Text(
subtitles[index],
style: const TextStyle(
fontSize: 15,
color: Colors.black54,
fontWeight: FontWeight.w300,
),
),
],
),
),
),
]),
),
);
}),
],
);
}
}
try this code will help you to start in OOP and make model to data in your app and you can add any thing to class like Icon ....
class FakeData {
final String title, subTitle, avatar, data;
bool onTapDown;
FakeData(
{required this.title,
this.onTapDown = false,
required this.subTitle,
required this.avatar,
required this.data});
}
this class collect data app and state for all item
class HomeState extends State<Homepage> {
final fakeData = [
FakeData(
title: 'AAA',
subTitle: '我去,额度这么高?',
avatar: "WechatIMG325.jpeg",
data: "昨天 18:08",
),
FakeData(
title: 'BBB',
subTitle: "XX,你上次给我兑换的烤箱还不错哦",
avatar: "WechatIMG326.jpeg",
data: "星期二",
),
FakeData(
title: 'CCC',
subTitle: "抱歉,我觉得我们不是很合适",
avatar: "WechatIMG327.jpeg",
data: "7月21日",
),
FakeData(
title: 'DDD',
subTitle: "邻居你好,你家的租户最近有点吵",
avatar: "WechatIMG328.jpeg",
data: "7月19日",
),
];
// final icons = [Icons.ac_unit, Icons.access_alarm, Icons.access_time];
#override
Widget build(BuildContext context) {
return Column(
children: [
...List.generate(fakeData.length, (index) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (TapDownDetails details) {
setState(() {
fakeData[index].onTapDown = !fakeData[index].onTapDown;
});
debugPrint("presed GestureDetector");
},
onTapUp: (TapUpDetails details) {
setState(() {
fakeData[index].onTapDown = !fakeData[index].onTapDown;
});
debugPrint("presed GestureDetector");
},
child: Container(
color: fakeData[index].onTapDown ? Colors.grey : Colors.white,
margin: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Row(children: [
SizedBox(
width: 50,
child: Container(
margin: const EdgeInsets.fromLTRB(0, 10, 0, 10),
child: ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child: Image.asset(
"assets/images/${fakeData[index].avatar}",
height: 50,
width: 50),
),
),
),
//const SizedBox(width: 10),
IntrinsicWidth(
child: Container(
margin: const EdgeInsets.fromLTRB(10, 0, 0, 0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
fakeData[index].title,
style: const TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.w300,
),
),
const SizedBox(height: 5),
Text(
fakeData[index].subTitle,
style: const TextStyle(
fontSize: 15,
color: Colors.black54,
fontWeight: FontWeight.w300,
),
),
],
),
),
),
]),
),
);
}),
],
);
}
}
The variable color needs to be outside the build method of the State, this code will reset the color to Colors.white on each build.
import 'package:flutter/material.dart';
class Homepage extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Homepage> {
final titles = ["AAA", "BBB", "CCC", "DDD"];
final subtitles = [
"我去,额度这么高?",
"XX,你上次给我兑换的烤箱还不错哦",
"抱歉,我觉得我们不是很合适",
"邻居你好,你家的租户最近有点吵"
];
final date = ["昨天 18:08", "星期二", "7月21日", "7月19日"];
final avatar = [
"WechatIMG325.jpeg",
"WechatIMG326.jpeg",
"WechatIMG327.jpeg",
"WechatIMG328.jpeg"
];
var color = Colors.white;
// final icons = [Icons.ac_unit, Icons.access_alarm, Icons.access_time];
#override
Widget build(BuildContext context) {
return Column(
children: [
...List.generate(titles.length, (index) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (TapDownDetails details) {
setState(() {
color = Colors.grey;
});
debugPrint("presed GestureDetector");
},
onTapUp: (TapUpDetails details) {
setState(() {
color = Colors.white;
});
debugPrint("presed GestureDetector");
},
child: Container(
color: color,
margin: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Row(children: [
SizedBox(
width: 50,
child: Container(
margin: const EdgeInsets.fromLTRB(0, 10, 0, 10),
child: ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child: Image.asset("assets/images/${avatar[index]}",
height: 50, width: 50),
),
),
),
//const SizedBox(width: 10),
IntrinsicWidth(
child: Container(
margin: const EdgeInsets.fromLTRB(10, 0, 0, 0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
titles[index],
style: const TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.w300,
),
),
const SizedBox(height: 5),
Text(
subtitles[index],
style: const TextStyle(
fontSize: 15,
color: Colors.black54,
fontWeight: FontWeight.w300,
),
),
],
),
),
),
]),
),
);
}),
],
);
}
}
The code above should work as our state is extracted outside of the build method and it would update correctly with each setState.
i want to edit a listview item when i click on it. I managed (with inkwell) that when I click on a listview item, the bottomsheet opens again where I also create new listview items, but I just can't edit it. I've tried everything I know (I don't know much I'm a beginner). here my codes.
--main.dart--
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import '/model/transaction.dart';
import '/widget/chart.dart';
import '/widget/new_transaction.dart';
import '/widget/transactoin_list.dart';
void main() {
// WidgetsFlutterBinding.ensureInitialized();
// SystemChrome.setPreferredOrientations(
// [
// DeviceOrientation.portraitUp,
// DeviceOrientation.portraitDown,
// ],
// );
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale("de"),
Locale("en"),
],
debugShowCheckedModeBanner: false,
title: "URLI",
theme: ThemeData(
primarySwatch: Colors.lightGreen,
fontFamily: "JosefinSans",
textTheme: ThemeData()
.textTheme
.copyWith(
headline4: const TextStyle(
fontFamily: "Tochter",
fontSize: 21,
),
headline5: const TextStyle(
fontFamily: "Bombing",
fontSize: 27,
letterSpacing: 3,
),
headline6: const TextStyle(
fontSize: 21,
fontWeight: FontWeight.w900,
),
)
.apply(
bodyColor: Colors.orangeAccent,
displayColor: Colors.orangeAccent.withOpacity(0.5),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
onPrimary: Colors.white,
primary: Theme.of(context).appBarTheme.backgroundColor,
textStyle: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
appBarTheme: const AppBarTheme(
titleTextStyle: TextStyle(
fontSize: 60,
fontFamily: "Tochter",
),
),
),
home: const AusgabenRechner(),
);
}
}
class AusgabenRechner extends StatefulWidget {
const AusgabenRechner({Key? key}) : super(key: key);
#override
State<AusgabenRechner> createState() => _AusgabenRechnerState();
}
class _AusgabenRechnerState extends State<AusgabenRechner> {
void _submitAddNewTransaction(BuildContext ctx) {
showModalBottomSheet(
context: ctx,
builder: (_) {
return GestureDetector(
onTap: () {},
child: NewTransaction(addNewTx: _addNewTransaction),
behavior: HitTestBehavior.opaque,
);
},
);
}
bool _showChart = false;
final List<Transaction> _userTransactions = [
// Transaction(
// id: "tx1",
// tittel: "Schuhe",
// preis: 99.99,
// datum: DateTime.now(),
// ),
// Transaction(
// id: "tx2",
// tittel: "Jacke",
// preis: 39.99,
// datum: DateTime.now(),
// ),
];
List<Transaction> get _recentTransactions {
return _userTransactions
.where(
(tx) => tx.datum.isAfter(
DateTime.now().subtract(
const Duration(days: 7),
),
),
)
.toList();
}
void _addNewTransaction(
String txTittel,
double txPreis,
DateTime choosenDate,
) {
final newTx = Transaction(
id: DateTime.now().toString(),
tittel: txTittel,
preis: txPreis,
datum: choosenDate,
);
setState(() {
_userTransactions.add(newTx);
});
}
void _deletedTransaction(String id) {
setState(() {
_userTransactions.removeWhere((tdddx) => tdddx.id == id);
});
}
#override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
final isInLandscape = mediaQuery.orientation == Orientation.landscape;
final appBar = AppBar(
centerTitle: true,
toolbarHeight: 99,
actions: [
IconButton(
onPressed: () => _submitAddNewTransaction(context),
icon: const Icon(
Icons.add,
color: Colors.white,
),
),
],
title: const Text(
"Ausgaben",
),
);
final txListWidget = SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.45,
child: TransactionList(
transaction: _userTransactions,
delettx: _deletedTransaction,
showNewTransaction: _submitAddNewTransaction,
),
);
return Scaffold(
appBar: appBar,
body: SingleChildScrollView(
child: Column(
children: [
if (isInLandscape)
SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.2,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Chart anzeigen",
style: Theme.of(context).textTheme.headline5,
),
const SizedBox(width: 9),
Switch.adaptive(
inactiveTrackColor:
Theme.of(context).primaryColor.withOpacity(0.3),
activeColor: Theme.of(context).primaryColor,
value: _showChart,
onChanged: (val) {
setState(() {
_showChart = val;
});
},
),
],
),
),
if (!isInLandscape)
SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.24,
child: Chart(
recentTransactions: _recentTransactions,
),
),
if (!isInLandscape)
SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.65,
child: txListWidget),
if (isInLandscape)
_showChart
? SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.51,
child: Chart(
recentTransactions: _recentTransactions,
),
)
: SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.81,
child: txListWidget)
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
child: const Icon(
Icons.add,
color: Colors.white,
),
onPressed: () => _submitAddNewTransaction(context),
),
);
}
}
--transaction_list.dart--
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '/model/transaction.dart';
class TransactionList extends StatefulWidget {
const TransactionList({
Key? key,
required this.transaction,
required this.delettx,
required this.showNewTransaction,
}) : super(key: key);
final List<Transaction> transaction;
final Function delettx;
final Function showNewTransaction;
#override
State<TransactionList> createState() => _TransactionListState();
}
class _TransactionListState extends State<TransactionList> {
#override
Widget build(BuildContext context) {
return widget.transaction.isEmpty
? LayoutBuilder(
builder: (ctx, contrains) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Keine Daten vorhanden!",
style: Theme.of(context).textTheme.headline6,
),
const SizedBox(
height: 30,
),
SizedBox(
height: contrains.maxHeight * 0.45,
child: Image.asset(
"assets/images/schlafen.png",
fit: BoxFit.cover,
),
)
],
);
},
)
: Align(
alignment: Alignment.topCenter,
child: ListView.builder(
shrinkWrap: true,
reverse: true,
itemCount: widget.transaction.length,
itemBuilder: (ctx, index) {
return InkWell(
onLongPress: () => widget.showNewTransaction(ctx),
child: Card(
elevation: 5,
child: ListTile(
leading: CircleAvatar(
radius: 33,
child: Padding(
padding: const EdgeInsets.all(9.0),
child: FittedBox(
child: Row(
children: [
Text(
widget.transaction[index].preis
.toStringAsFixed(2),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
),
),
const Text(
"€",
style: TextStyle(
fontSize: 21,
),
)
],
),
),
),
),
title: Text(
widget.transaction[index].tittel,
style: Theme.of(context).textTheme.headline6,
),
subtitle: Text(
DateFormat.yMMMMd("de")
.format(widget.transaction[index].datum),
style: Theme.of(context).textTheme.headline4,
),
trailing: MediaQuery.of(context).size.width > 460
? TextButton.icon(
onPressed: () =>
widget.delettx(widget.transaction[index].id),
icon: const Icon(
Icons.delete_outline,
),
label: const Text("Löschen"),
style: TextButton.styleFrom(
primary: Colors.red,
),
)
: IconButton(
onPressed: () =>
widget.delettx(widget.transaction[index].id),
icon: const Icon(
Icons.delete_outline,
color: Colors.red,
),
),
),
),
);
},
),
);
}
}
--new_transaction.dart--
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class NewTransaction extends StatefulWidget {
const NewTransaction({Key? key, required this.addNewTx}) : super(key: key);
final Function addNewTx;
#override
State<NewTransaction> createState() => _NewTransactionState();
}
class _NewTransactionState extends State<NewTransaction> {
final _tittelcontroller = TextEditingController();
final _preiscontroller = TextEditingController();
DateTime? _selectedDate;
void _submitData() {
final enteredTittel = _tittelcontroller.text;
final enteredPreis = double.parse(_preiscontroller.text);
if (_preiscontroller.text.isEmpty) {
return;
}
if (enteredTittel.isEmpty || enteredPreis <= 0 || _selectedDate == null) {
return;
}
widget.addNewTx(
_tittelcontroller.text,
double.parse(_preiscontroller.text),
_selectedDate,
);
Navigator.of(context).pop();
}
void _presentDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2022),
lastDate: DateTime.now(),
).then((pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
bottom: false,
child: SingleChildScrollView(
child: Container(
//height: MediaQuery.of(context).size.height * 0.5,
padding: EdgeInsets.only(
top: 10,
left: 18,
right: 18,
bottom: MediaQuery.of(context).viewInsets.bottom + 10,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
TextButton(
onPressed: _submitData,
child: Text(
"hinzufügen",
style: Theme.of(context).textTheme.headlineSmall,
),
),
TextField(
controller: _tittelcontroller,
onSubmitted: (_) => _submitData(),
decoration: const InputDecoration(
label: Text("Tittel"),
),
),
TextField(
controller: _preiscontroller,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
onSubmitted: (_) => _submitData(),
decoration: const InputDecoration(
label: Text("Preis"),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 66),
child: Center(
child: Column(
children: [
Text(
_selectedDate == null
? "Kein Datum ausgewählt"
: DateFormat.yMMMMEEEEd("de")
.format(_selectedDate!),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
const SizedBox(
height: 21,
),
ElevatedButton(
style: Theme.of(context).elevatedButtonTheme.style,
onPressed: _presentDatePicker,
child: const Text("Datum wählen"),
),
],
),
),
)
],
),
),
),
);
}
}
EN: Here is an excample code, how i would solve this, when i understood the problem. The passing of the variables to the new Class is a bit differnent then in your Code but it works the same.
DE: So hier ist jetzt ein Beispielcode, wie ich es lösen würde, wenn ich das Problem richtig verstanden habe, dabei werden die Variabeln etwas anders als bei dir in die neue Klasse übergeben, funktioniert aber gleich
class testListview extends StatefulWidget {
var transaction;
var delettx;
var showNewTransaction;
//Passing the data to new Class
testListview(this.transaction, this.delettx,
this.showNewTransaction);
#override
State<testListview> createState() => _testListviewState();
}
class _testListviewState extends State<testListview> {
var transaction;
var delettx;
var showNewTransaction;
//Pass the data into the State of the new Class
_testListviewState(this.transaction, this.delettx,
this.showNewTransaction);
var transaction_2;
//The init state will be called in the first initialization of
//the Class
#override
void initState() {
//Pass your transactions to a new variable
setState(() {
transaction_2 = transaction;
});
super.initState();
}
#override
Widget build(BuildContext context) {
return ListView.builder(itemBuilder: (BuildContext context,
index){
return TextButton(onPressed: (){
//Change the data with onPressed
setState(() {
transaction_2["preis"] = "500";
});
}, child: Text(transaction_2["preis"]));
});}}
I concede that there are many questions similar to mine, but I have not found a satisfactory answer from those questions. So I decided to make my own question specifying my problem. I have 3 BloCs in my program each with different purposes. They all share similar problems, as such I will ask on one of those BloCs with the hope that one solution will fix all of the BloCs.
The problem is this, if I just started the application and have logged in, the BloC will update the UI. If I have logged in, exited the app, and restarted it, the Bloc will not update the UI. The Bloc in question is called DetailpersonilBloc with 1 event called Detail and 2 states called DetailpersonilInitial and Loaded. At the event of Detail, the state Loaded should be emitted.
I called Detail at LoginPage and at GajiPage at initState. This works when I just opened the app, but does not work when I restart the app. I also have equatable thinking that it will help me but apparently it changes nothing.
Note: The "..." at the GajiPage is just some code that I believe is not necessary for reproduction.
DetailpersonilBloc
part 'detailpersonil_event.dart';
part 'detailpersonil_state.dart';
class DetailpersonilBloc
extends Bloc<DetailpersonilEvent, DetailpersonilState> {
DetailpersonilBloc() : super(const DetailpersonilInitial()) {
on<Detail>((event, emit) async {
SharedPreferences pref = await SharedPreferences.getInstance();
String name = pref.getString('nama');
String nrp = pref.getString('NRP');
String pangkat = pref.getString('pangkat');
String jabatan = pref.getString('jabatan');
String satker = pref.getString('satker');
String polda = pref.getString('polda');
String npwp = pref.getString('NPWP');
String rekening = pref.getString('rekening');
String bank = pref.getString('bank');
emit(Loaded(
name,
nrp,
pangkat,
jabatan,
satker,
polda,
npwp,
rekening,
bank,
));
});
}
}
DetailpersonilEvent
part of 'detailpersonil_bloc.dart';
#immutable
abstract class DetailpersonilEvent extends Equatable {}
class Detail extends DetailpersonilEvent {
#override
List<Object> get props => [];
}
DetailpersonilState
part of 'detailpersonil_bloc.dart';
#immutable
abstract class DetailpersonilState extends Equatable {
final String nama;
final String nrp;
final String pangkat;
final String jabatan;
final String satker;
final String polda;
final String npwp;
final String rekening;
final String bank;
const DetailpersonilState(
{this.nama,
this.nrp,
this.pangkat,
this.jabatan,
this.satker,
this.polda,
this.npwp,
this.rekening,
this.bank});
}
class DetailpersonilInitial extends DetailpersonilState {
const DetailpersonilInitial()
: super(
nama: 'Nama',
nrp: 'NRP',
pangkat: 'Pangkat',
jabatan: 'Jabatan',
satker: 'Satker',
polda: 'Polda',
npwp: 'NPWP',
rekening: 'No Rekening',
bank: 'Nama Bank',
);
#override
List<Object> get props =>
[nama, nrp, pangkat, jabatan, satker, polda, npwp, rekening, bank];
}
class Loaded extends DetailpersonilState {
const Loaded(
String nama,
String nrp,
String pangkat,
String jabatan,
String satker,
String polda,
String npwp,
String rekening,
String bank,
) : super(
nama: nama,
nrp: nrp,
pangkat: pangkat,
jabatan: jabatan,
satker: satker,
polda: polda,
npwp: npwp,
rekening: rekening,
bank: bank);
#override
List<Object> get props =>
[nama, nrp, pangkat, jabatan, satker, polda, npwp, rekening, bank];
}
LoginPage
class LoginPage extends StatefulWidget {
const LoginPage({Key key}) : super(key: key);
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
double width = 0;
double height = 0;
TextEditingController nrpController = TextEditingController();
TextEditingController sandiController = TextEditingController();
final formKey = GlobalKey<FormState>();
DetailpersonilBloc detailPersonilBloc;
GajiBloc gajiBloc;
TunkinBloc tunkinBloc;
bool passwordVisible = false;
#override
void initState() {
super.initState();
checkToken();
}
void checkToken() async {
SharedPreferences pref = await SharedPreferences.getInstance();
if (pref.getString('token') != null) {
detailPersonilBloc = DetailpersonilBloc();
gajiBloc = GajiBloc();
tunkinBloc = TunkinBloc();
detailPersonilBloc.add(Detail());
gajiBloc.add(Gaji());
tunkinBloc.add(Tunkin());
Navigator.push(
context, MaterialPageRoute(builder: (context) => const MainPage()));
}
}
onLogin(DetailpersonilBloc detailPersonilBloc) async {
if (formKey.currentState.validate()) {
var token = await APIService.generateToken(
nrpController.text, sandiController.text);
if (token != null) {
SharedPreferences pref = await SharedPreferences.getInstance();
await pref.setString('token', token.token);
var detail = await APIService.getDetailPersonil(token.token);
await pref.setString('nama', detail.nMPEG);
await pref.setString('NRP', detail.nRP);
await pref.setString('pangkat', detail.nMGOL1);
await pref.setString('jabatan', detail.sEBUTJAB);
await pref.setString('satker', detail.nMSATKER);
await pref.setString('polda', detail.nMUAPPAW);
await pref.setString('NPWP', detail.nPWP);
await pref.setString('rekening', detail.rEKENING);
await pref.setString('bank', detail.nMBANK);
nrpController.clear();
sandiController.clear();
detailPersonilBloc.add(Detail());
Navigator.push(
context, MaterialPageRoute(builder: (context) => const MainPage()));
} else {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Error'),
content: Text('Login Gagal'),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Tutup'),
),
],
);
},
);
}
}
}
#override
Widget build(BuildContext context) {
var detailPersonilBloc = BlocProvider.of<DetailpersonilBloc>(context);
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height;
return Scaffold(
body: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
Opacity(
opacity: 0.5,
child: Image(
image: AssetImage('images/bg-map-min.png'),
),
),
],
),
SingleChildScrollView(
padding: EdgeInsets.only(top: 100),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 100,
width: width,
child: const Image(
image: AssetImage('images/login-logo.png'),
alignment: Alignment.center,
),
),
Container(
padding: const EdgeInsets.all(15),
child: const Text(
'GAJI DAN TUNKIN',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
Form(
key: formKey,
child: Column(
children: [
Container(
margin: const EdgeInsets.all(20 - 2.6),
child: Card(
elevation: 10,
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.only(bottom: 20),
child: const Text(
'LOGIN',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: const EdgeInsets.only(bottom: 25),
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan NRP/NIP';
}
return null;
},
controller: nrpController,
decoration: InputDecoration(
labelText: 'NRP/NIP',
hintText: 'Masukkan NRP/NIP',
prefixIcon: Icon(Icons.person,
color: Colors.blue.shade700),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
TextFormField(
obscureText: !passwordVisible,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan Kata Sandi';
}
return null;
},
controller: sandiController,
decoration: InputDecoration(
labelText: 'Kata Sandi',
hintText: 'Masukkan Kata Sandi',
prefixIcon: Icon(Icons.lock,
color: Colors.blue.shade700),
suffixIcon: IconButton(
onPressed: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
icon: Icon(
passwordVisible
? Icons.visibility
: Icons.visibility_off,
color: Colors.blue.shade700,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
)
],
),
),
),
),
ElevatedButton(
onPressed: () async {
await onLogin(detailPersonilBloc);
},
child: const Text('LOGIN'),
style: ElevatedButton.styleFrom(
primary: Colors.blue.shade700,
minimumSize: const Size(200, 40),
),
)
],
),
),
],
),
),
],
),
);
}
}
GajiPage
class GajiPage extends StatefulWidget {
const GajiPage({Key key}) : super(key: key);
#override
_GajiPageState createState() => _GajiPageState();
}
class _GajiPageState extends State<GajiPage> {
double width = 0;
double height = 0;
var currentYear = DateTime.now().year;
var currentMonth = DateTime.now().month;
DetailpersonilBloc detailPersonilBloc;
GajiBloc gajiBloc;
#override
void initState() {
setState(() {
detailPersonilBloc = DetailpersonilBloc();
detailPersonilBloc.add(Detail());
setMonth();
setYear();
gajiBloc = GajiBloc();
gajiBloc.add(Gaji());
});
super.initState();
}
#override
Widget build(BuildContext context) {
var detailPersonilBloc = BlocProvider.of<DetailpersonilBloc>(context);
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Image(
image: const AssetImage('images/header-logo.png'),
width: width / 2,
),
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(255, 170, 177, 175),
Color.fromARGB(255, 197, 217, 212)
],
),
),
),
),
body: Stack(
children: [
BlocBuilder<GajiBloc, GajiState>(
builder: (context, state) {
return state is GajiLoaded
? ListView(
children: [
Container(
height: 100,
),
Card(
color: const Color.fromARGB(255, 74, 50, 152),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.all(10),
child: const Text(
'Gaji Bersih',
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
),
),
Container(
margin: const EdgeInsets.all(10),
child: Text(
NumberFormat.currency(
locale: 'en',
symbol: 'RP ',
decimalDigits: 0)
.format(state.bersih),
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 40,
color: Colors.white,
),
),
),
],
),
),
Card(
child: Column(
children: [
Container(
color: const Color.fromARGB(255, 238, 238, 238),
width: width,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Container(
margin: const EdgeInsets.fromLTRB(
10, 10, 0, 10),
width: (width / 2) - 25,
child: const Text(
'Detail Gaji',
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
),
),
),
Container(
margin: const EdgeInsets.fromLTRB(
5, 10, 20, 10),
width: (width / 2) - 18,
child: Text(
'${state.bulan} - ${state.tahun}',
textAlign: TextAlign.end,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
),
),
)
],
),
),
...
],
),
),
Container(
height: 50,
),
],
)
: Center(
child: Text(
'Tidak ada data. Data gaji bulan ${state.bulan} belum diproses'),
);
},
),
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Center(
child: BlocBuilder<DetailpersonilBloc, DetailpersonilState>(
builder: (context, state) {
return Card(
child: Row(
children: [
Container(
margin: const EdgeInsets.all(10),
child: const CircleAvatar(
backgroundImage: AssetImage('images/Profpic.PNG'),
radius: 30,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 250,
padding: const EdgeInsets.all(5),
child: Text(
state.nama,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold),
)),
Container(
padding: const EdgeInsets.all(5),
child: Text(
state.nrp,
style: const TextStyle(fontSize: 15),
)),
],
),
GestureDetector(
onTap: () {
detailPersonilBloc.add(Detail());
showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) => detailsBottomSheet(),
);
},
child: const Text(
'DETAILS',
style: TextStyle(color: Colors.blue),
),
)
],
),
);
},
),
),
GestureDetector(
onTap: () {
showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, StateSetter setState) {
return filterBottomSheet();
},
);
},
);
},
child: Container(
height: 50,
width: width,
decoration: const BoxDecoration(
color: Color.fromARGB(255, 244, 244, 244),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
margin: const EdgeInsets.only(right: 3),
child: const Icon(
Icons.tune,
color: Color.fromARGB(255, 45, 165, 217),
),
),
const Text(
'Filter',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20,
color: Color.fromARGB(255, 45, 165, 217),
),
),
],
),
),
)
],
)
],
),
);
}
}
Note 2: The "..." is a bunch of code not needed for reproduction.
The answer is surprisingly simple as I would learn from my internship. The reason the Bloc is not updating is because I was not using the context of the application. So when I generated a new Bloc inside my pages, it was "empty". So the solution is to use context.read().add(BlocEvent()) or create a new bloc with BlocProvider.of(context) then add the event. Basically the bloc has to be provided with the original context of the application.
The issue is that progress icons and continue button are hidden in my stepper (cancel button is visible). So when i click on the place where "continue" button should exists (next to Cancel button) the "continue" event is happening and its correct.
The strange thing is that when i use the same code in a new test project, i can see both buttons and icons. Maybe its related to my sidebar but i am not sure.
import 'package:flutter/material.dart';
import 'package:taamin/bloc/navigation_bloc/navigation_bloc.dart';
class MyOrdersPage extends StatefulWidget with NavigationStates{
#override
_MyOrdersPageState createState() => _MyOrdersPageState();
}
class _MyOrdersPageState extends State<MyOrdersPage> {
List<Step> steps = [
Step(
title: const Text('New Account'),
isActive: true,
state: StepState.complete,
content: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Email Address'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Password'),
),
],
),
),
Step(
title: const Text('Address'),
isActive: true,
state: StepState.editing,
content: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Home Address'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Postcode'),
),
],
),
),
Step(
state: StepState.error,
title: const Text('Avatar'),
subtitle: const Text("Error!"),
content: Column(
children: <Widget>[
CircleAvatar(
backgroundColor: Colors.red,
)
],
),
),
];
StepperType stepperType = StepperType.vertical;
int currentStep = 0;
bool complete = false;
next() {
currentStep + 1 != steps.length
? goTo(currentStep + 1)
: setState(() => complete = true);
}
cancel() {
if (currentStep > 0) {
goTo(currentStep - 1);
}
}
goTo(int step) {
setState(() => currentStep = step);
}
switchStepType() {
setState(() => stepperType == StepperType.horizontal
? stepperType = StepperType.vertical
: stepperType = StepperType.horizontal);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Assurance Véhicule'),
),
body: Column(children: <Widget>[
complete ? Expanded(
child: Center(
child: AlertDialog(
title: new Text("Profile Created"),
content: new Text(
"Tada!",
),
actions: <Widget>[
new FlatButton(
child: new Text("Close"),
onPressed: () {
setState(() => complete = false);
},
),
],
),
),
)
: Expanded(
child: Stepper(
steps: steps,
type: stepperType,
currentStep: currentStep,
onStepContinue: next,
onStepTapped: (step) => goTo(step),
onStepCancel: cancel,
),
),
]),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.list),
onPressed: switchStepType,
),
);
}
}
and here is the code of my sidebar menu:
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:rxdart/rxdart.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:taamin/bloc/navigation_bloc/navigation_bloc.dart';
import 'package:taamin/sidebar/menu_item.dart';
class SideBar extends StatefulWidget {
#override
_SideBarState createState() => _SideBarState();
}
class _SideBarState extends State<SideBar>
with SingleTickerProviderStateMixin<SideBar> {
AnimationController _animationController;
StreamController<bool> isSidebarOpenedStreamController;
Stream<bool> isSidebarOpenedStream;
StreamSink<bool> isSidebarOpenedSink;
final _animationDuration = const Duration(milliseconds: 500);
#override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: _animationDuration);
isSidebarOpenedStreamController = PublishSubject<bool>();
isSidebarOpenedStream = isSidebarOpenedStreamController.stream;
isSidebarOpenedSink = isSidebarOpenedStreamController.sink;
}
#override
void dispose() {
_animationController.dispose();
isSidebarOpenedStreamController.close();
isSidebarOpenedSink.close();
super.dispose();
}
void onIconPressed() {
final animationStatus = _animationController.status;
final isAnimationCompleted = animationStatus == AnimationStatus.completed;
//completed means sidebar is open
if (isAnimationCompleted) {
isSidebarOpenedSink.add(false);
_animationController.reverse();
} else {
isSidebarOpenedSink.add(true);
_animationController.forward();
}
}
#override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return StreamBuilder<bool>(
initialData: false,
stream: isSidebarOpenedStream,
builder: (context, isSideBarOpenedAsync) {
return AnimatedPositioned(
duration: _animationDuration,
top: 0,
bottom: 0,
left: isSideBarOpenedAsync.data ? 0 : -screenWidth,
right: isSideBarOpenedAsync.data ? 0 : screenWidth - 30,
child: Row(
children: <Widget>[
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
color: const Color(0xFF262AAA),
child: Column(
children: <Widget>[
SizedBox(
height: 100,
),
ListTile(
title: Text(
"Yassine",
style: TextStyle(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w800),
),
subtitle: Text(
"firstname.name#gmail.com",
style: TextStyle(
color: Color(0xFF1BB5FD),
fontSize: 15,
),
),
leading: CircleAvatar(
child: Icon(
Icons.perm_identity,
color: Colors.white,
),
radius: 40,
),
),
Divider(
height: 64,
thickness: 0.5,
color: Colors.white.withOpacity(0.3),
indent: 32,
endIndent: 32,
),
MenuItem(
icon: Icons.home,
title: "Home",
onTap: () {
onIconPressed();
BlocProvider.of<NavigationBloc>(context).add(NavigationEvents.HomePageClickedEvent);
},
),
MenuItem(
icon: Icons.person,
title: "My Accounts",
onTap: () {
onIconPressed();
BlocProvider.of<NavigationBloc>(context).add(NavigationEvents.MyAccountClickedEvent);
},
),
MenuItem(
icon: Icons.directions_car,
title: "Assurance Véhicule",
onTap: () {
onIconPressed();
BlocProvider.of<NavigationBloc>(context).add(NavigationEvents.MyOrdersClickedEvent);
},
),
MenuItem(
icon: Icons.card_giftcard,
title: "Wishlist",
),
Divider(
height: 64,
thickness: 0.5,
color: Colors.white.withOpacity(0.3),
indent: 32,
endIndent: 32,
),
MenuItem(
icon: Icons.settings,
title: "Settings",
),
MenuItem(
icon: Icons.exit_to_app,
title: "Logout",
onTap: () {
onIconPressed();
exit(0);
}
),
],
),
),
),
Align(
alignment: Alignment(0, -0.9),
child: GestureDetector(
onTap: () {
onIconPressed();
},
child: ClipPath(
clipper: CustomMenuClipper(),
child: Container(
width: 35,
height: 110,
color: Color(0xFF262AAA),
alignment: Alignment.centerLeft,
child: AnimatedIcon(
progress: _animationController.view,
icon: AnimatedIcons.menu_close,
color: Color(0xFF1BB5FD),
size: 25,
),
),
),
),
)
],
),
);
},
);
}
}
class CustomMenuClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
Paint paint = Paint();
paint.color = Colors.white;
final width = size.width;
final height = size.height;
Path path = Path();
path.moveTo(0, 0);
path.quadraticBezierTo(0, 8, 10, 16);
path.quadraticBezierTo(width - 1, height / 2 - 20, width, height / 2);
path.quadraticBezierTo(width + 1, height / 2 + 20, 10, height - 16);
path.quadraticBezierTo(0, height - 8, 0, height);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return false;
}
}
add controlsBuilder inside stepper
controlsBuilder: (BuildContext context,
{VoidCallback onStepContinue, VoidCallback onStepCancel}) {
return Row(
children: <Widget>[
FlatButton(
onPressed: onStepContinue,
child: const Text('Continue',
style: TextStyle(color: Colors.white)),
color: Colors.redAccent,
),
new Padding(
padding: new EdgeInsets.all(10),
),
FlatButton(
onPressed: onStepCancel,
child: const Text(
'Cancel',
style: TextStyle(color: Colors.white),
),
color: Colors.black,
),
],
);
},