I use flutter_local_notifications package for my app. When I tap on notification, payload appears with a black background and I have to press return button several times in order to see app's page. Background becomes brighter in every time I press button. What is the problem?
class _StatefulListTileState extends State<StatefulListTile> {
Color _iconColor1 = Colors.white;
Color _iconColor2 = Colors.yellow;
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
#override
initState() {
super.initState();
var initializationSettingsAndroid =
new AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettingsIOS = new IOSInitializationSettings();
var initializationSettings = new InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
}
#override
Widget build(BuildContext context) {
return new Container(
child: new FutureBuilder(
future: saveColors(),
builder: (context, snapshot) {
return new ListTile(
title: new Text(widget.title),
leading: new IconButton(
icon: Icon(Icons.star, color: snapshot.data.contains('${widget.id}') ? _iconColor2: _iconColor1),
onPressed: () {
setState(() {
if (snapshot.data.contains('${widget.id}')) {
_iconColor2 = Colors.white;
_iconColor1 = Colors.white;
flutterLocalNotificationsPlugin.cancel(widget.id);
snapshot.data.remove('${widget.id}');
} else {
_iconColor1 = Colors.yellow;
_iconColor2 = Colors.yellow;
_showNotification(widget.id, widget.day, widget.clock);
snapshot.data.add('${widget.id}');
}});},),);}));}
Future onSelectNotification(String payload) async {
showDialog(
context: context,
builder: (_) {
return new AlertDialog(...
);},);}
Future _showNotification(id, day, clock) async {
//determine hour, minute and d
var time = new Time(hour, minute, 0);
var androidPlatformChannelSpecifics =
new AndroidNotificationDetails('show weekly channel id',
'show weekly channel name', 'show weekly description');
var iOSPlatformChannelSpecifics =
new IOSNotificationDetails();
var platformChannelSpecifics = new NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime(
id,
widget.title,
widget.team,
d,
time,
platformChannelSpecifics,
payload: widget.title + '\n' + widget.team + '\n' + widget.channel);
}
}
Any help you can give me would be appreciated.
Make sure you are doing this only once in your code
#override
initState() {
super.initState();
var initializationSettingsAndroid =
new AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettingsIOS = new IOSInitializationSettings();
var initializationSettings = new InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
}
Related
I am following this 5 minutes video to set up an audio recorder in Flutter.
When I click the ElevatedButton to start recording the audio, it does change between play and stop, and an audio file is created, but the snapshot.hasData is always false, so the Text stays 00:00 during recording. The only information I found is about setSubscriptionDuration, which I did set. I also tried flutter clean, etc. What else can it be?
I'm using Flutter 3.3.8, on macOS, flutter_sound: ^9.1.9. I'm running the app on a real iPhone XR with flutter run
I am new to flutter. I really appreciate any help you can provide!
I have
StreamBuilder
StreamBuilder<RecordingDisposition>(
stream: recorder.onProgress,
builder: (context, snapshot) {
print('snapshot.hasData :${snapshot.hasData}');
final duration =
snapshot.hasData ? snapshot.data!.duration : Duration.zero;
print('duration :$duration');
String twoDigits(int n) => n.toString().padLeft(2, '0');
final twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
final twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return Text(
'$twoDigitMinutes:$twoDigitSeconds',
style: const TextStyle(
fontSize: 20,
),
);
},
),
ElevatedButton
ElevatedButton(
child: Icon(
recorder.isRecording ? Icons.stop : Icons.mic,
size: 20,
),
onPressed: () async {
if (recorder.isRecording) {
await stop();
} else {
await record();
}
setState(() {});
},
)
Initialize the recorder properly
final recorder = FlutterSoundRecorder();
Future<void> initRecorder() async {
final status = await Permission.microphone.request();
if (status != PermissionStatus.granted) {
throw 'Microphone permission not granted';
}
await recorder.openRecorder();
isRecorderReady = true;
recorder.setSubscriptionDuration(
const Duration(
microseconds: 100,
),
);
}
#override
void initState() {
super.initState();
initRecorder();
}
This is what it looks like so far:
So, I found a solution, but the StreamBuilder question is not answered. Instead of using StreamBuilder, create a stateful TimerWidget that's initialized by a ValueNotifier.
import 'dart:async';
import 'package:flutter/material.dart';
enum Time { start, pause, reset }
class TimerController extends ValueNotifier<Time> {
TimerController({Time time = Time.reset}) : super(time);
void startTimer() => value = Time.start;
void pauseTimer() => value = Time.pause;
void resetTimer() => value = Time.reset;
}
class TimerWidget extends StatefulWidget {
final TimerController controller;
const TimerWidget({
Key? key,
required this.controller,
}) : super(key: key);
#override
_TimerWidgetState createState() => _TimerWidgetState();
}
class _TimerWidgetState extends State<TimerWidget> {
Duration duration = const Duration();
Timer? timer;
#override
void initState() {
super.initState();
widget.controller.addListener(() {
switch (widget.controller.value) {
case Time.start:
startTimer();
break;
case Time.pause:
stopTimer();
break;
case Time.reset:
reset();
stopTimer();
break;
}
});
}
void reset() => setState(() => duration = const Duration());
void addTime() {
const addSeconds = 1;
setState(() {
final seconds = duration.inSeconds + addSeconds;
if (seconds < 0) {
timer?.cancel();
} else {
duration = Duration(seconds: seconds);
}
});
}
void startTimer({bool resets = true}) {
if (!mounted) return;
timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime());
}
void stopTimer() {
if (!mounted) return;
setState(() => timer?.cancel());
}
#override
Widget build(BuildContext context) => Center(child: buildTime());
Widget buildTime() {
String twoDigits(int n) => n.toString().padLeft(2, "0");
final twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
final twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return Text(
'$twoDigitMinutes:$twoDigitSeconds',
style: const TextStyle(
fontSize: 80,
fontWeight: FontWeight.bold,
),
);
}
}
change the microseconds: 100, to millisecond:100 in recorder.setSubscriptionDuration
recorder.setSubscriptionDuration(
const Duration(milliseconds: 100),
);
like this
I'm trying to change the state of isSyncing then rebuild the widget with set state once await api.fetchProducts() is completed. api.fetchProducts() is what i used to fetch from API then store local using sqflite.
I tried using cloudSyn.then() but it wont work.
class SyncProgress extends StatefulWidget {
#override
_SyncProgressState createState() => _SyncProgressState();
}
class _SyncProgressState extends State<SyncProgress> {
bool isSyncing = true;
String progressString = 'Syncing your data....';
final db = DatabaseHelper();
final bloc = ProductBloc();
#override
void initState() {
super.initState();
}
Future cloudSync() async{
await api.fetchProducts();
//Here is the challenge
setState(() {
isSyncing = false;
progressString = 'Syncing complete....';
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: isSyncing ? _indicateProgress() : _syncDone()
);
}
Widget _indicateProgress(){
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
SizedBox(height: 50.0,),
Text(progressString, style: TextStyle(
fontSize: 16.0,
),),
],
),
);
}
_syncDone(){
print('Syncing completed');
//return Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage()));
}
}
Use then to force setState function to execute only after fetchProducts() is finished:
Future cloudSync() async{
await api.fetchProducts().then(
setState(() {
isSyncing = false;
progressString = 'Syncing complete....';
});
);
}
I have a problem with the Flutter Text To Speech package.
When clicking on a FloatingActionButton I would like to speak/play several Strings (with different Speechrates) subsequently. However, when doing so, I can only hear the last string that I have passed onto the function and not the first one.
As you can see in the code below, I have tried to make use of the asynchronus programming (async / await).
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_tts/flutter_tts.dart';
class SpeakerClass extends StatefulWidget{
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _SpeakerClassState();
}
}
class _SpeakerClassState extends State<SpeakerClass>{
String text1 = 'eins';
String text2 = 'zwei';
String text3 = 'drei';
String text4 = 'vier';
String currentTtsString;
double ttsSpeechRate1 = 0.5;
double ttsSpeechRate2 = 1.0;
double currentSpeechRate;
Future playTtsString1() async {
currentTtsString = text1;
currentSpeechRate = ttsSpeechRate1;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
Future playTtsString2() async {
currentTtsString = text2;
currentSpeechRate = ttsSpeechRate2;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FloatingActionButton (
backgroundColor: Colors.blue,
child: Icon(Icons.volume_up, color: Colors.white),
onPressed: () async {
await playTtsString1();
await playTtsString2();
},
)
);
}
}
Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
FlutterTts flutterTts;
flutterTts = new FlutterTts();
await flutterTts.setLanguage("en-GB");
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
await flutterTts.isLanguageAvailable("en-GB");
await flutterTts.setSpeechRate(currentSpeechRate);
await flutterTts.speak(currentTtsString);
}
When pressing the FloatingActionButton I expect the program to first carry out the function playTtsString1 ("eins" with a speed of 0.5) and afterwards the function playTtsString2 ("zwei" with a speed of 1).
However, somehow I can only hear the program saying "zwei". I guess the program is not waiting for the first function "playTtsString1" to be finished and already carries out the second function "playTtsString2". I would really appreciate any help on this matter!
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_tts/flutter_tts.dart';
class SpeakerClass extends StatefulWidget{
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _SpeakerClassState();
}
}
class _SpeakerClassState extends State<SpeakerClass>{
String text1 = 'eins';
String text2 = 'zwei';
String text3 = 'drei';
String text4 = 'vier';
String currentTtsString;
double ttsSpeechRate1 = 0.5;
double ttsSpeechRate2 = 1.0;
double currentSpeechRate;
FlutterTts flutterTts;
bool bolSpeaking = false;
Future playTtsString1() async {
bolSpeaking = true;
currentTtsString = text1;
currentSpeechRate = ttsSpeechRate1;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
Future playTtsString2() async {
bolSpeaking = true;
currentTtsString = text2;
currentSpeechRate = ttsSpeechRate2;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FloatingActionButton (
backgroundColor: Colors.blue,
child: Icon(Icons.volume_up, color: Colors.white),
onPressed: () async {
// Play String 1
await playTtsString1();
// Check String 1 Finish
new Future.delayed(new Duration(milliseconds: 100), () async {
// loop until bolSpeaking = false
while (bolSpeaking) {
await Thread.sleep(100);
}
// play String 2
await playTtsString2();
}
},
)
);
}
}
Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
flutterTts = new FlutterTts();
await flutterTts.setLanguage("en-GB");
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
await flutterTts.isLanguageAvailable("en-GB");
await flutterTts.setSpeechRate(currentSpeechRate);
flutterTts.setCompletionHandler(() {
setState(() {
// The following code(s) will be called when the TTS finishes speaking
bolSpeaking = false;
});
});
flutterTts.speak(currentTtsString);
}
This should now work with the latest flutter_tts version.
You simply need to set awaitSpeakCompletion before the speaking happens.
You can update your run method like so:
Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
FlutterTts flutterTts;
flutterTts = new FlutterTts();
await flutterTts.awaitSpeakCompletion(true);
await flutterTts.setLanguage("en-GB");
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
await flutterTts.isLanguageAvailable("en-GB");
await flutterTts.setSpeechRate(currentSpeechRate);
await flutterTts.speak(currentTtsString);
}
it works, can show the addTask page
var task = await Navigator.of(context).pushNamed('/addTask');
don't work, does not change the page.
String task = await Navigator.of(context).pushNamed('/addTask');
more code:
class TodoListState extends State<TodoList> {
//....
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Todo List')),
body: _taskList.isEmpty ? emptyView("No task") : _buildTodoList(),
floatingActionButton: new FloatingActionButton(
onPressed: _pushAddTodoScreen,
tooltip: 'Add task',
child: new Icon(Icons.add),
),
);
}
void _pushAddTodoScreen() async {
var task = await Navigator.of(context).pushNamed('/addTask');
_addTask(task);
}
void _addTask(String taskTitle) async {
AppDatabase appDatabase = AppDatabase.get();
await appDatabase.insertTask(taskTitle);
_updateTasks();
}
}
and how to see the log in the android studio, my app is running on a ios simulator
Try with explicit types:
String task = await Navigator.of(context).pushNamed<String>('/addTask');
The ListView shows Item1, Item2 and Item3.
When I try to delete Item2, the ListView incorrectly shows Item1 and Item2.
The console shows the correct items in the list: Item1 and Item3.
HomeScreen:
class HomeScreen extends StatefulWidget {
#override
HomeScreenState createState() => new HomeScreenState();
}
class HomeScreenState extends State<HomeScreen> {
List<Todo> todos = new List();
#override
void initState() {
super.initState();
populateTodos();
}
void populateTodos() async {
TodoDatabase db = new TodoDatabase();
db.getAllTodos().then((newTodos) {
for (var todo in newTodos) {
print(todo.title + ", " + todo.id);
}
setState(() => todos = newTodos);
});
}
void openAddTodoScreen() async {
Navigator
.push(context,
new MaterialPageRoute(builder: (context) => new AddTodoScreen()))
.then((b) {
populateTodos();
});
}
void clearDb() async {
TodoDatabase db = new TodoDatabase();
db.clearDb();
populateTodos();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Todo App"),
actions: <Widget>[
new IconButton(
icon: new Icon(Icons.delete),
onPressed: () => clearDb(),
),
new IconButton(
icon: new Icon(Icons.refresh),
onPressed: () => populateTodos(),
)
],
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Expanded(
child: new ListView.builder(
padding: new EdgeInsets.all(10.0),
itemCount: todos.length,
itemBuilder: (BuildContext context, int index) {
return new TodoItem(todos[index], onDelete: (id) {
TodoDatabase db = new TodoDatabase();
print("ID: " + id);
db.deleteTodo(id).then((b) {
populateTodos();
});
});
},
),
)
],
),
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add), onPressed: () => openAddTodoScreen()),
);
}
}
DataBase Code:
class TodoDatabase {
TodoDatabase();
static Database _db;
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDB();
return _db;
}
Future<Database> initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "main.db");
var theDb = await openDatabase(path, version: 1, onCreate: createDatabase);
return theDb;
}
void createDatabase(Database db, int version) async {
await db.execute("CREATE TABLE Todos(id STRING PRIMARY KEY, title TEXT, description TEXT)");
print("Database was Created!");
}
Future<List<Todo>> getAllTodos() async {
var dbClient = await db;
List<Map> res = await dbClient.query("Todos");
print(res);
return res.map((map) => new Todo(title: map["title"], description: map["description"], id: map["id"])).toList();
}
Future<Todo> getTodo(String id) async {
var dbClient = await db;
var res = await dbClient.query("Todos", where: "id = ?", whereArgs: [id]);
if (res.length == 0) return null;
return new Todo.fromDb(res[0]);
}
Future<int> addTodo(Todo todo) async {
var dbClient = await db;
int res = await dbClient.insert("Todos", todo.toMap());
return res;
}
Future<int> updateTodo(Todo todo) async {
var dbClient = await db;
int res = await dbClient.update(
"Todos",
todo.toMap(),
where: "id = ?",
whereArgs: [todo.id]);
return res;
}
Future<int> deleteTodo(String id) async {
var dbClient = await db;
var res = await dbClient.delete(
"Todos",
where: "id = ?",
whereArgs: [id]);
print("Deleted item");
return res;
}
Future<int> clearDb() async {
var dbClient = await db;
var res = await dbClient.execute("DELETE from Todos");
print("Deleted db contents");
return res;
}
}
This almost seems like a bug with Flutter.
I have to add some more text because otherwise it won't let me post.
I don't know how to illustrate the issue further.
Thanks for your help!
I was missing a key.
Here's the correct ListView.builder:
new ListView.builder(
key: new Key(randomString(20)),
padding: new EdgeInsets.all(10.0),
itemCount: todos.length,
itemBuilder: (BuildContext context, int index) {
return new TodoItem(todos[index], onDelete: (id) {
TodoDatabase db = new TodoDatabase();
db.deleteTodo(id).then((b) {
populateTodos();
});
});
},
),