Related
I am making a music player using Flutter and 'audio player' and I want to have 'previous' and 'next' button to play previous and next music.
Currently, I have sepearated classes for each music and when I connected them with 'Navigator.push' to show previous and next music easily.
But problem is for example, when I click B music in the music list and, in B music player page, there are previous and next buttons.
Previous button leads to A music page and next button leads to C music page. When I navigate pages with those buttons, route, for example, can be something like music list->B->C->B->A->B.
And then if I click 'back' button on the app bar which was created because of stateful widget itself, it doesn't go back to music list but goes all pages I accessed back one by one like B->A->B->C->B->music list.
I want it to go to music list directly.
So I am wondering whether I need to put all musics in one class not all sepearated classes or I need to changes the 'navigation' function.
But I'm not sure what solution actually works.
Could someone help me with this? Thank you
Below is the code of one music
class Music1 extends StatefulWidget {
const Music1({Key? key}) : super(key: key);
#override
State<Music1> createState() => _Music1State();
}
class _Music1State extends State<Music1> {
//setting the project url
String img_cover_url =
"https://i.pinimg.com/736x/a7/a9/cb/a7a9cbcefc58f5b677d8c480cf4ddc5d.jpg";
late AudioPlayer advancedPlayer;
Duration _duration = new Duration();
Duration _position = new Duration();
bool isPlaying = false;
bool isPaused = false;
bool isLoop = false;
void initPlayer() async {
await advancedPlayer.setSource(AssetSource("forest.mp3"));
advancedPlayer.onDurationChanged.listen((d) {setState(() {
_duration = d;
}); });
advancedPlayer.onPositionChanged.listen((p) {setState(() {
_position = p;
}); });
}
//init the player
#override
void initState() {
// TODO: implement initState
super.initState();
advancedPlayer = AudioPlayer();
initPlayer();
}
#override
void changeToSecond(int second) {
Duration newDuration = Duration(seconds: second);
advancedPlayer.seek(newDuration);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Musics for Kids", style: TextStyle(fontFamily: "Itim",fontSize: 25)),),
body: Stack(
children: [
Container(
constraints: const BoxConstraints.expand(),
height: 300.0,
width: 300.0,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/forest.jpg"),
fit: BoxFit.cover,
),
),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 28, sigmaY: 28),
child: Container(
color: Colors.black.withOpacity(0.6),
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//setting the music cover
ClipRRect(
borderRadius: BorderRadius.circular(30.0),
child: Image.asset(
"assets/forest.jpg",
width: 250.0,
),
),
const SizedBox(
height: 10.0,
),
const Text(
"Forest",
style: TextStyle(
color: Colors.white, fontSize: 36, letterSpacing: 6, fontFamily: "Itim"),
),
//Setting the seekbar
const SizedBox(
height: 30.0, //50
),
Row( //problem part - previous and next buttons
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute<void>(builder: (BuildContext context) {
return const Music0();
})
);
},
onTap: () => Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context)=> MusicList(),
),
(route) => true,
),
child: Text("Previous", style: TextStyle(color: Colors.white)),
),
SizedBox(width: 200),
GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute<void>(builder: (BuildContext context) {
return const Music2();
})
);
},
child: Text("Next", style: TextStyle(color: Colors.white)),
),
],
),
const SizedBox(
height: 20.0, //50
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
_position.toString().split(".")[0],
style: const TextStyle(
color: Colors.white,
),
),
Slider.adaptive(
onChanged: (value){},
min: 0.0,
max: _duration.inSeconds.toDouble(),
value:_position.inSeconds.toDouble(),
onChangeEnd: (double value){
setState((){
changeToSecond(value.toInt());
value = value;
});
advancedPlayer.pause();
advancedPlayer.seek(Duration(seconds: value.toInt()));
advancedPlayer.resume();
},
activeColor: Colors.white,
),
Text(
_duration.toString().split(".")[0],
style: const TextStyle(
color: Colors.white,
),
),
],
),
const SizedBox(
height: 60.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(60.0),
color: Colors.black87,
border: Border.all(color: Colors.white38),
),
width: 50.0,
height: 50.0,
child: InkWell(
onTapDown: (details) {
advancedPlayer.setPlaybackRate(0.5);
},
onTapUp: (details) {
advancedPlayer.setPlaybackRate(1);
},
child: const Center(
child: Icon(
Icons.fast_rewind_rounded,
color: Colors.white,
),
),
),
),
const SizedBox(
width: 60.0,
),
Container(
width: 50.0,
height: 50.0,
decoration:
BoxDecoration(
borderRadius: BorderRadius.circular(60.0),
color: Colors.black87,
border: Border.all(color: Colors.pink),
),
child: InkWell(
onTap: () async{
if (isPlaying) {
await advancedPlayer.pause();
setState((){
isPlaying = false;
});
} else {
await advancedPlayer.resume();
setState((){
isPlaying = true;
});
}
},
child: Icon(
isPlaying ? Icons.pause: Icons.play_arrow,
color: Colors.white,
),
)
),
const SizedBox(
width: 60.0,
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(60.0),
color: Colors.black87,
border: Border.all(color: Colors.white38),
),
width: 50.0,
height: 50.0,
child: InkWell(
onTapDown: (details) {
advancedPlayer.setPlaybackRate(2);
},
onTapUp: (details) {
advancedPlayer.setPlaybackRate(1);
},
child: const Center(
child: Icon(
Icons.fast_forward_rounded,
color: Colors.white,
),
),
),
),
],
)
],
),
],
),
);
}
}
EDIT: if you don't want the option I gave below, you could do this, and it will pop the screens until you reach the right screen:
Navigator.popUntil(context, ModalRoute.withName('/example'));
An alternative:
I think I didn't uderstand very well, but do you really need to each music be a different screen? Because you could make only one Music class, and just one screen for the player, and just move between the musics.
You could just setup a list of musics, and functions to move between one from another. Without having to make many different screens for each.
Example of Music Class:
class Music {
String imgCoverUrl;
Duration duration;
String assetSource;
Music({
required this.imgCoverUrl,
required this.duration,
required this.assetSource,
});
}
Example of the screen with functions:
class MusicPlayerScreen extends StatefulWidget {
const MusicPlayerScreen({super.key});
#override
State<MusicPlayerScreen> createState() => _MusicPlayerScreenState();
}
class _MusicPlayerScreenState extends State<MusicPlayerScreen> {
// variable to keep track of which music is playing right now
int selectedMusicIndex = 0;
// list of your musics, so you can move between one and another
List<Music> myMusics = [
Music(imgCoverUrl: 'cover_1.png', duration: Duration(seconds: 10), assetSource: 'myFolder/song1.mp3'),
Music(imgCoverUrl: 'cover_2.png', duration: Duration(seconds: 10), assetSource: 'myFolder/song2.mp3'),
Music(imgCoverUrl: 'cover_2.png', duration: Duration(seconds: 10), assetSource: 'myFolder/song3.mp3'),
];
// function to move to the next music, verifying if have any music after the actual one
void nextMusic() {
if (selectedMusicIndex + 1 < myMusics.length) {
setState(() {
selectMusicIndex++;
});
}
}
// function to move to the previous music, verifying if isn't the first music
void previousMusic() {
if (selectMusic != 0) {
setState(() {
selectMusicIndex--;
});
}
}
#override
Widget build(BuildContext context) {
return Container();
}
}
Then you just use the following the get each music attributes:
myMusics[selectedMusicIndex].imgCoverUrl;
myMusics[selectedMusicIndex].duration;
myMusics[selectedMusicIndex].assetSource;
So as you can see here's my profile page i just want to create an update method here. Is it possible to do inside this page for the update method? I'm still new with flutter development here. So if there's any method that can update inside this page it would be really helpful for me thank you. Because usually I only see that people would create a new page as for the database page but what I do with the login and register is that I do inside the page. So I just tryna see if there's a way to create update method inside a profile page
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_user_profile/model/parking.dart';
class ParkingPage extends StatefulWidget {
#override
State<ParkingPage> createState() => _ParkingPageState();
}
class _ParkingPageState extends State<ParkingPage> {
User? user = FirebaseAuth.instance.currentUser;
Parking loginuser = Parking();
#override
void initState(){
super.initState();
FirebaseFirestore.instance
.collection('parkingTech')
.doc(user!.uid)
.get()
.then((value){
this.loginuser = Parking.fromMap(value.data());
setState(() {});
});
}
// #override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF121212),
appBar: AppBar(
backgroundColor: Color(0xFF121212),
elevation: 0.0,
title: Text('Parking Tech',
style:TextStyle(
color: Color(0xFFFFFFFF),
fontWeight: FontWeight.bold,
),
),
centerTitle:true,
actions: [
// Icon(Icons.menu,
// color:Colors.amber,
// size:40,
// )
]
),
body: Padding(
padding: const EdgeInsets.all(30.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children:[
Row(
mainAxisAlignment: MainAxisAlignment.center ,
children:[
CircleAvatar(
backgroundColor: Colors.amber,
radius: 100.0,
child: Center(
child: Text('P',
style:TextStyle(
fontSize: 80,
color: Color(0xFF121212),
fontWeight: FontWeight.bold,
),
),
)
),
],
),
SizedBox(height: 15),
Text("Parking time : ${loginuser.name} ",
style:TextStyle(
fontSize: 20,
color: Colors.grey,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 10),
Text('00 : 56 : 05',
style:TextStyle(
fontSize: 50,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 15),
Text('P15 AED',
style:TextStyle(
fontSize: 30,
color: Colors.blueGrey,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 15),
Text('On Parking ',
style:TextStyle(
fontSize: 15,
color: Colors.amber,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 15),
Text('End Parking',
style:TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 12),
Text('Extend Parking',
style:TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 12),
],
),
),
);
}
}
If I understand your use case correctly, you want to update your widget whenever the user object changes? That is what I suspect since you are calling it a 'profile page', and it seems like the data in this widget is dependent on the user.
You could listen to changes to the Firebase user object, and in the callback, you could then rebuild your widget. Here is the relevant snippet from the documentation, adapted to your use case (untested):
#override
void initState() {
super.initState();
FirebaseAuth.instance.userChanges().asyncMap((User? user) =>
FirebaseFirestore.instance
.collection('parkingTech')
.doc(user!.uid)
.get()
.then((value) {
this.loginuser = Parking.fromMap(value.data());
setState(() {});
}));
}
I have a form textfield and i want to put textbutton as a suffix for i forgot password. But when i try add i cant see anything.
My Code:
TextFormField(
decoration: InputDecoration(
suffix:TextButton(child: Text("Şifremi Unuttum?"),onPressed: (){})
),
floatingLabelStyle: TextStyle(
color: isFocused
? Color.fromARGB(255, 141, 56, 211)
: Color.fromARGB(255, 91, 103, 125),
fontWeight: FontWeight.w500,
fontSize: 14),
),
),
When i check suffix wants a widget. So i gave it but its not showing what is the problem in here ? Thanks for helps!!
Everything works for me with your source code. The TextButton is displayed correctly. I will attach the code below, as well as a screenshot, where I added the missing label, because you use styling for it and a condition if you forgot the password, then display a text button in the suffix:
class _MyHomePageState extends State<MyHomePage> {
static bool isFocused = true;
static bool isForgotPassword = true;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("App bar"),
),
body: TextFormField(
initialValue: "My Text",
decoration: InputDecoration(
suffix: isForgotPassword ? TextButton(
child: const Text("Şifremi Unuttum?"), onPressed: () {}) : null,
label: const Text(
"Floating Label",
style: TextStyle(fontSize: 25),
),
floatingLabelStyle: TextStyle(
color: isFocused
? const Color.fromARGB(255, 141, 56, 211)
: const Color.fromARGB(255, 91, 103, 125),
fontWeight: FontWeight.w500,
fontSize: 14),
),
));
}
}
Screenshot of app screen with 2 false booleans
In PageViewBuilder, instead of swipe gesture I want to move to the next page using onTap on an ArgonTimerButton which I found on pub.dev. I created a controller and used controller.nextPage() but it gives me this error:
'positions.isNotEmpty': PageController.page cannot be accessed before a PageView is built with it.
My code:
final controller = PageController();
#override
Widget build(BuildContext context) {
MediaQueryData size = MediaQuery.of(context).size;
return PageView.builder(
physics: NeverScrollableScrollPhysics(),
controller: PageController(),
itemCount: widget.workoutExcercises.length,
itemBuilder: (context, index) {
return
//other widgets
ArgonTimerButton(
initialTimer: 3, // Optional
height: 50,
width: size.width * 0.55,
minWidth: width * 0.40,
color: Colors.white,
borderRadius: 5.0,
curve: Curves.easeInToLinear,
child: Text(
"Done?",
style: TextStyle(
color: Colors.black,
fontSize: 24,
fontWeight: FontWeight.w700),
),
loader: (timeLeft) {
return Text(
"Rest Time | $timeLeft",
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.w700),
);
},
onTap: (startTimer, btnState) {
if (btnState == ButtonState.Idle) {
controller.nextPage(duration: Duration(milliseconds:1000), curve: Curves.easeInToLinear);
}
},
),
],
),
);
},
);
How do I solve this?
You are creating the controller but you are not using it.
Instead of :
controller: PageController(),
Do:
controller: controller,
I am just learning and can't wrap my head around something.
I am building a simple app but the app requires the first thing shown is a splash screen of some sorts.
Upon tapping the only button on the SplashScreen, ideally it would load the rest of the app however I also want my app inside to work around a bottomNavBar.
I have done the Bottom Navigation Bar on my own and it works so I can cycle between my pages, but my main.dart is pointing towards my Splash_Screen. Where as in the Nav model I am using, main points to the Nav.dart file.
How do I get my app to launch in this sequence: Splash_Screen --> when buttom tapped --> go inside where Bottom Navigation Bar will be leading to it's respective 3 pages.
Thanks in advance.
EDIT: CODE
main.dart
import 'package:flutter/material.dart';
import 'package:offroad/screens/splash_screen.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen(),
);
}
}
SplashScreen.dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:offroad/screens/home_screen.dart';
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
Color mainColor = Color(0xFFF1330A);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/splash.jpg'),
fit: BoxFit.cover,
),
),
child: Container(
color: Colors.black54,
child: Stack(
children: <Widget>[
Container(),
Positioned(
bottom: 90,
child: Container(
width: MediaQuery.of(context).size.width,
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => Home(),
),
);
},
child: Container(
margin: EdgeInsets.symmetric(horizontal: 80),
height: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: mainColor,
),
child: Center(
child: Text(
'Entrar',
style: TextStyle(
fontSize: 25,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
),
Positioned(
bottom: 230,
child: Container(
width: MediaQuery.of(context).size.width,
child: Text(
'Tu mundo 4x4\n empieza aqui!',
style: GoogleFonts.amiri(
height: 1.2,
textStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 40,
),
),
textAlign: TextAlign.center,
),
),
),
],
),
),
),
);
}
}
Once my splash page loads and the button is tapped I want it to now go inside and load this, which is what I had working seperately but with the main.dart pointing to the "nav.dart"
Not sure what you are trying to achieve exactly but you should use the routes or onGenerateRoute in MaterialApp to define your routes.
In that case you can get rid of the home and set the initial route to Splash Screen and the default as nav.
You can then navigate to your tab component using a named route defined in your routes or onGenerateRoute.