Background music in Flutter does not work - flutter

I'm trying to add background music to my app. if i start my app the music starts rightly, but if i press a button which should have no impact to the music, the music starts from new. i code in Flutter.Here is my code i cutted the unimportant things away.
import 'package:audioplayers/audio_cache.dart';
import 'package:audioplayers/audioplayers.dart';
class _MyHomepageState extends State<MyHomepage> {
AudioPlayer player = AudioPlayer();
AudioCache cache = new AudioCache();
bool isPlaying = false;
Future<bool> _willPopCallback() async {
if (isPlaying == false) {
setState(() {
isPlaying = true;
});
player.stop();
}
return true;
}
openingActions() async {
player = await cache.loop('audio/test.mp3');
}
#override
Widget build(BuildContext context) {
openingActions();
return WillPopScope(
onWillPop: () => _willPopCallback(),
child: Scaffold(
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/background.jpg'),
fit: BoxFit.cover,
),
),
...
...
...
raisedbutton(
....
)

You can copy paste run full code below
You can move openingActions(); from build to initState
And rebuild will not call openingActions(); again
#override
void initState() {
openingActions();
super.initState();
}
#override
Widget build(BuildContext context) {
//openingActions(); //delete this line and move to initState
full code
import 'package:flutter/material.dart';
import 'package:audioplayers/audio_cache.dart';
import 'package:audioplayers/audioplayers.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
AudioPlayer player = AudioPlayer();
AudioCache cache = new AudioCache();
bool isPlaying = false;
Future<bool> _willPopCallback() async {
if (isPlaying == false) {
setState(() {
isPlaying = true;
});
player.stop();
}
return true;
}
openingActions() async {
player = await cache.loop('audio/test.mp3');
}
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
void initState() {
openingActions();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Open route'),
onPressed: () {
setState(() {});
},
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Related

Flutter: Update the UI with value from an async function

I want to see a the value of a counter in a flutter UI when the counter is updated asynchronously.
Staring from the sample flutter project, I would expect the below would make it, but only the final value is displayed. How can I achieve to see the numbers changing from 1 to 100000?
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() async {
for(int i=0; i<100000; ++i) {
setState(() {
_counter++;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
I think the issue is just that your loop is running too fast to show the intermediate values. Slowing the loop down with Future.delayed() should let you see what you want.
void _incrementCounter() async {
for(int i=0; i<100000; ++i) {
await Future.delayed(Duration(seconds: 1));
setState(() {
_counter++;
});
}
}
to see the numbers changing from 1 to 100000 You can use Timer.periodic.
Creating state level timer variable to have control on running state.
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
Timer? _timer;
void _incrementCounter() async {
const delay = Duration(milliseconds: 100); // controll update speed
const numberLimit = 100000;
_timer = Timer.periodic(delay, (timer) {
if (_counter < numberLimit) {
setState(() {
_counter++;
});
} else {
timer.cancel();
}
});
}
void _reset() {
setState(() {
_counter = 0;
});
_timer?.cancel();
}
#override
void dispose() {
_timer?.cancel();
super.dispose();
}
You can find more about dart-async-library and Timer.periodic on flutter.dev.
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late Timer _timer;
int _start = 0;
void startTimer() {
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) => setState(() {
if (_start > 100000) {
timer.cancel();
} else {
_start = _start + 1;
}
}));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_start',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: startTimer,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Hey you can use ValueListenableBuilder to notify you state instead of calling setState as it will rebuild whole ui. Read here in more details about ValueListenableBuilder
Below is sample code -
class _MyHomePageState extends State<MyHomePage> {
Timer? _timer;
ValueNotifier _valueNotifier = ValueNotifier(0);
#override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: _valueNotifier,
builder: (context, value, child) {
return Text(value.toString());
},
);
}
void _incrementCounter() async {
const delay = Duration(milliseconds: 100); // controll update speed
const numberLimit = 100000;
_timer = Timer.periodic(delay, (timer) {
if (_valueNotifier.value < numberLimit) {
_valueNotifier.value++;
} else {
timer.cancel();
}
});
}
void _reset() {
_valueNotifier.value = 0;
_timer?.cancel();
}
#override
void dispose() {
_timer?.cancel();
_valueNotifier.dispose();
super.dispose();
}

Flutter - How to trigger an animation inside a child widget from its parent widget [duplicate]

Let said I have a widget "mySonWidget" inside this widget I have a function "updateIconColor", I want to call that function from the father of "mySonWidget"
I did something similiar with callback but this is not the same scenario.
I saw people other widgets doing similar thing with controllers, but I don't know how create a custom controller.
How can I do it?
HELP
class mySonWidget extends StatefulWidget {
#override
_mySonWidgetState createState() => _mySonWidgetState();
}
class _mySonWidgetState extends State<mySonWidget> {
var _iconColor = Colors.red[500];
void updateIconColor() {
setState(() {
print('updateIconColor was called');
_iconColor = Colors.green;
});
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (Icon(Icons.star)),
color: _iconColor,
onPressed: () {},
),
);
}
}
father example:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(children: <Widget>[
mySonWidget(),
FlatButton(
onPressed: () {
/*..Call Function inside mySonWidget (updateIconColor) ..*/
},
child: Text(
"Change Color",
),
),
]),
),
);
}
}
You can copy paste run full code below
You can use GlobalKey can use _key.currentState to call updateIconColor()
code snippet
GlobalKey _key = GlobalKey();
...
mySonWidget(key: _key),
FlatButton(
onPressed: () {
final _mySonWidgetState _state = _key.currentState;
_state.updateIconColor();
},
...
class mySonWidget extends StatefulWidget {
mySonWidget({Key key}) : super(key: key);
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
GlobalKey _key = GlobalKey();
#override
Widget build(BuildContext context) {
return MaterialApp(
//theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
body: Column(children: <Widget>[
mySonWidget(key: _key),
FlatButton(
onPressed: () {
final _mySonWidgetState _state = _key.currentState;
_state.updateIconColor();
},
child: Text(
"Change Color",
),
),
]),
),
),
);
}
}
class mySonWidget extends StatefulWidget {
mySonWidget({Key key}) : super(key: key);
#override
_mySonWidgetState createState() => _mySonWidgetState();
}
class _mySonWidgetState extends State<mySonWidget> {
var _iconColor = Colors.red[500];
void updateIconColor() {
setState(() {
print('updateIconColor was called');
_iconColor = Colors.green;
});
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (Icon(Icons.star)),
color: _iconColor,
onPressed: () {},
),
);
}
}

Flutter RawKeyboardListener does not work in release mode

I'm using RawKeyboardListener to capture keyboard events on web, it works fine in debug mode but when I build it for release it does not capture keyboard events. I tried it with a basic app:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Future<void> _onEventKey(RawKeyEvent event) async {
if (event.runtimeType.toString() == 'RawKeyDownEvent') {
if (event.isKeyPressed(LogicalKeyboardKey.arrowLeft)) {
_incrementCounter();
}
}
}
#override
Widget build(BuildContext context) {
return RawKeyboardListener(
focusNode: FocusNode(),
onKey: (RawKeyEvent event) async {
await _onEventKey(event);
},
autofocus: true,
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
),
);
}
}
Is there anything i'm doing wrong?
Since generated javascript code is minified in release mode, there is no more type RawKeyDownEvent, but something like minified:qN.
Instead of
if (event.runtimeType.toString() == 'RawKeyDownEvent') {
you have to use a more accurate comparison:
if (event.runtimeType == RawKeyDownEvent) {
Here is fixed code (also removed unnecessary async/await):
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _onEventKey(RawKeyEvent event) {
// next line prints something like 'minified:qN' in production mode
print(event.runtimeType.toString());
// if (event.runtimeType.toString() == 'RawKeyDownEvent') {
if (event.runtimeType == RawKeyDownEvent) {
if (event.isKeyPressed(LogicalKeyboardKey.arrowLeft)) {
_incrementCounter();
}
}
}
#override
Widget build(BuildContext context) {
return RawKeyboardListener(
focusNode: FocusNode(),
onKey: (RawKeyEvent event) {
_onEventKey(event);
},
autofocus: true,
child: Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
Text('$_counter', style: Theme.of(context).textTheme.headline4),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
);
}
}

How can I make the initial value of a TextFormField equal to a variable loaded from shared preferences? (Flutter)

I want to make the initial value of the TextFormField equal to the counter variable. The counter is maintained between app restarts, but when I restart the app, the initial value of the text field is always 0.
Is there a better way of doing that?
(I'm new to programming, sorry if it's a dumb question)
Here's the code I used.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Shared preferences demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Shared preferences demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
void initState() {
super.initState();
_loadCounter();
}
//Loading counter value on start
_loadCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_counter = (prefs.getInt('counter') ?? 0);
});
}
//Incrementing counter after click
_incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_counter = (prefs.getInt('counter') ?? 0) + 1;
prefs.setInt('counter', _counter);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
TextFormField(
initialValue: '$_counter',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Try adding a controller for TextFormField and update the value after getting it from SharedPreferences.
Something like this.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Shared preferences demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Shared preferences demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
int _counter = 0;
final myController = TextEditingController();
#override
void dispose() {
myController.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
_loadCounter();
}
_loadCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_counter = (prefs.getInt('counter') ?? 0);
myController.text = _counter.toString();
});
}
_incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_counter = (prefs.getInt('counter') ?? 0) + 1;
prefs.setInt('counter', _counter);
myController.text = _counter.toString();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
TextFormField(
controller: myController,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Hope this solves your issue.

Returning States from an async Task to the FutureBuilder

I want to return states to the FutureBuilder from the async task while it's running.
The Task is carProvider.run().
Currently, while waiting for the task to finish, there is a CircularProgressIndicator with a text underneath that display the message string.
And i want to constantly update this message string.
class CarDealerStatefulWidgetState extends State<CarDealerStatefulWidget> {
String _message = "Initialising database and fetching data...";
set setMessage(String message) => setState(() {
_message = message;
});
String get getMessage => _message;
static CarDataProvider carProvider = CarDataProvider();
Future<List<Car>> _requestResult = carProvider.run();
List<Car> items;
String path;
var currentPage;
Widget build(BuildContext context) {
PageController controller;
return DefaultTextStyle(
style: Theme.of(context).textTheme.headline2,
textAlign: TextAlign.center,
child: FutureBuilder<List<Car>>(
future: _requestResult,
builder: (BuildContext context, AsyncSnapshot<List<Car>> snapshot) {
Widget child;
if (snapshot.hasData) {
//
} else if (snapshot.hasError) {
child = Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
);
} else {
child = Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.green),
),
Padding(
padding: const EdgeInsets.only(top: 20.0),
),
Text(getMessage,
style: TextStyle(
fontSize: 13,
))
I would solve your problem without a FutureProvider but using setState instead.
Check this working example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _ready = false;
String _text = "";
void changeText(String text) {
setState(() {
_text = text;
});
}
void setReady(bool ready) {
setState(() {
_ready = ready;
});
}
#override
void initState() {
super.initState();
loadData();
}
Future loadData() async {
changeText("initializing...");
await Future.delayed(const Duration(milliseconds: 2000), () => {});
changeText("load settings...");
await Future.delayed(const Duration(milliseconds: 2000), () => {});
changeText("connect to database...");
await Future.delayed(const Duration(milliseconds: 2000), () => {});
changeText("init GUI...");
await Future.delayed(const Duration(milliseconds: 2000), () => {});
setReady(true);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: !_ready
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
Text(_text),
],
)
: Text("OK ready!"),
));
}
}