I tried the official documentation and also tried following a lot of different guides but somehow i am unable to be able to show a simple local notification on my app. I am confused with completely different lines of code being shown on different guides. My app runs fine without the local notification implementation.
It is a simple app which fetches some data from an API and should display a notification whenever any of the computed data is > 0.
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future main() async {
//WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MyWin App',
theme: ThemeData.dark(
//primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'CoWin paid vaccine availability for 18+'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('icons8_coronavirus_64.png');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings();
final InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
macOS: null);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: selectNotification);
fetchAvailability();
getData();
}
Future selectNotification(String payload) async {
//Handle notification tapped logic here
}
int day = 0;
int sumApolloGariahat = 0;
int sumApolloBypass = 0;
int sumWoodlands = 0;
List<int> availableAtApolloBypass = [];
List<int> availableAtApolloGariahat = [];
List<int> availableAtWoodlands = [];
var url = Uri.parse(
'https://cdn-api.co-vin.in/api/v2/appointment/sessions/public/calendarByDistrict?district_id=725&date=17-05-2021');
Future<http.Response> fetchAvailability() {
return http.get(url);
}
Future getData() async {
var response = await http.get(url);
if (response.statusCode == 200) {
String data = response.body;
Map decodedData = jsonDecode(data);
var apolloGariahatID = decodedData['centers'][11]['center_id'];
var apolloBypassID = decodedData['centers'][16]['center_id'];
var woodlandsID = decodedData['centers'][23]['center_id'];
for (day = 0;
day < decodedData['centers'][12]['sessions'].length;
day++) {
availableAtApolloGariahat.add(decodedData['centers'][12]['sessions']
[day]['available_capacity_dose1']);
}
for (day = 0;
day < decodedData['centers'][16]['sessions'].length;
day++) {
availableAtApolloBypass.add(decodedData['centers'][16]['sessions'][day]
['available_capacity_dose1']);
}
for (day = 0;
day < decodedData['centers'][27]['sessions'].length;
day++) {
availableAtWoodlands.add(decodedData['centers'][27]['sessions'][day]
['available_capacity_dose1']);
}
availableAtApolloGariahat.forEach((e) => sumApolloGariahat += e);
availableAtApolloBypass.forEach((e) => sumApolloBypass += e);
availableAtWoodlands.forEach((e) => sumWoodlands += e);
print('Available at Apollo Gariahat: $sumApolloGariahat');
print('Available at Apollo EM Bypass: $sumApolloBypass');
print('Available at Woodlands: $sumWoodlands');
} else {
print(response.statusCode);
}
}
Future showNotification() async {
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'your channel id', 'your channel name', 'your channel description',
importance: Importance.max,
priority: Priority.high,
showWhen: false);
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0, 'plain title', 'plain body', platformChannelSpecifics,
payload: 'item x');
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Available at Apollo Gariahat: $sumApolloGariahat',
style: TextStyle(fontSize: 20.0),
),
Text(
'Available at Apollo EM Bypass: $sumApolloBypass',
style: TextStyle(fontSize: 20.0),
),
Text(
'Available at Woodlands: $sumWoodlands',
style: TextStyle(fontSize: 20.0),
),
TextButton(
onPressed: showNotification,
child: Text('Show Notification'),
),
],
),
),
);
}
}
I think there is an issue with the initialisation of your plugin.
You have given your android settings as this.
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('icons8_coronavirus_64.png');
You have probably forgot to include this image asset directly in the android/app/src/main/res/drawable folder.
As mentioned in the documentation of flutter_local_notifications, for showing notification on android, all images need to be added into the drawable folder.
I have added my own icon.png into the drawable folder like this,
The, I used it in my initialization, like this
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('icon.png');
This fixed the issue for me and I can now see your sample notification.
Related
I have a serious problem with my Riverpod. Specifically, I am using StateProvider in Riverpod package. But when I update state, the widget tree does not rebuild. I checked the new state whether is updated by printing out state to see, I see that they are actually updated.
I have some same situations but when I click hot restart/reload page/scroll up,down mouse to change size chrome window, the widget tree rebuild one time.
Please help me and explain everything the most detail and easy to understand. Thank you very much
new state print out but UI not update
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:math';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class Data {
final String data;
Data({required this.data});
}
final helloWorldProvider = StateProvider<Data?>((ref) => Data(data: 'No data'));
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
#override
ConsumerState<MyApp> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
#override
void initState() {
// TODO: implement initState4
print("Init state");
super.initState();
// getData();
}
// getData() async {
// // http.Response response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
// // final title = jsonDecode(response.body)["title"];;
// // ref.read(helloWorldProvider.notifier).update((state) => title);
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString('valueTemp', 'newValue');
// String? valueTemp = prefs.getString('valueTemp');
// String value = valueTemp ?? '';
// Data data = Data(data: value);
// ref.read(helloWorldProvider.notifier).update((state) => data);
// print("Đã thực hiện xong");
// }
void _change() {
print("change");
final rawString = generateRandomString(5);
Data data = new Data(data: rawString);
ref.watch(helloWorldProvider.notifier).update((state) => data);
print(ref.read(helloWorldProvider.notifier).state?.data);
}
String generateRandomString(int len) {
var r = Random();
return String.fromCharCodes(List.generate(len, (index) => r.nextInt(33) + 89));
}
#override
Widget build(BuildContext context) {
print('Rebuild');
final data = ref.watch(helloWorldProvider.notifier).state;
final dataText = data?.data ?? 'No text';
print(dataText);
return MaterialApp(
title: 'Google Docs Clone',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Column(children: [
Text(dataText)
]
)
),
floatingActionButton: FloatingActionButton(
onPressed: _change,
tooltip: 'Change',
child: const Icon(Icons.add),
),
));
}
}
I don't want to use other pattern as Provider, Bloc, StateNotifierProvider, ChangeNotifierProvider... I only want to run StateProvider successfully. I have refered to many articles and stackoverflows answer but I did't found any useful helps to my case.
final data = ref.watch(helloWorldProvider.notifier).state;
is watching the notifier, which rarely changes. You want to watch the state change, as in:
final data = ref.watch(helloWorldProvider);
Fixed, Tested your code.
I recommend this article Flutter Riverpod 2.0: The Ultimate Guide to Advanced Riverpod 😀
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:math';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class Data {
final String data;
Data({required this.data});
}
final helloWorldProvider = StateProvider<Data?>((ref) => Data(data: 'No data'));
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
#override
ConsumerState<MyApp> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
#override
void initState() {
// TODO: implement initState4
print("Init state");
super.initState();
// getData();
}
// getData() async {
// // http.Response response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
// // final title = jsonDecode(response.body)["title"];;
// // ref.read(helloWorldProvider.notifier).update((state) => title);
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString('valueTemp', 'newValue');
// String? valueTemp = prefs.getString('valueTemp');
// String value = valueTemp ?? '';
// Data data = Data(data: value);
// ref.read(helloWorldProvider.notifier).update((state) => data);
// print("Đã thực hiện xong");
// }
void _change() {
print("change");
final rawString = generateRandomString(5);
Data data = Data(data: rawString);
ref.read(helloWorldProvider.notifier).update((state) => data);
print(ref.read(helloWorldProvider.notifier).state?.data);
}
String generateRandomString(int len) {
var r = Random();
return String.fromCharCodes(
List.generate(len, (index) => r.nextInt(33) + 89));
}
#override
Widget build(BuildContext context) {
print('Rebuild');
final data = ref.watch(helloWorldProvider)?.data;
final dataText = data ?? 'No text';
print(dataText);
return MaterialApp(
title: 'Google Docs Clone',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Column(children: [Text(dataText)]),
),
floatingActionButton: FloatingActionButton(
onPressed: _change,
tooltip: 'Change',
child: const Icon(Icons.add),
),
),
);
}
}
I have developed an image uploading API and called it an async function I need a spinner to make load until it fetches the result and after fetching the result it has to show the response received in an alert box
uploadImage() async {
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send().then((value) => null);
_isInAsyncCall = true;
http.Response res = await http.Response.fromStream(response);
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
});
My whole flutter code is here
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'find waste collector'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File? selectedImage;
String? message;
bool _isInAsyncCall = false;
uploadImage() async {
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send().then((value) => null);
_isInAsyncCall = true;
http.Response res = await http.Response.fromStream(response);
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
});
}
Future getImage() async {
final pickedImage =
await ImagePicker().pickImage(source: ImageSource.gallery);
selectedImage = File(pickedImage!.path);
setState(() {});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
selectedImage == null
? Text("please pick a image to upload")
: Image.file(selectedImage!),
inAsyncCall: _isInAsyncCall,
// demo of some additional parameters
opacity: 0.5,
progressIndicator: CircularProgressIndicator(),
TextButton.icon(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromARGB(255, 62, 187, 0))),
onPressed: uploadImage,
icon: Icon(Icons.upload_file_rounded, color: Colors.black38),
label: Text("Upload waste sample Image",
style: TextStyle(color: Colors.black)))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
child: Icon(Icons.add_a_photo),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}
modal_progress_hud library helped lot on making this task
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_progress_hud/flutter_progress_hud.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.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: 'find waste collector'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File? selectedImage;
String? message;
bool showSpinner = false;
uploadImage() async {
setState(() {
showSpinner = true;
});
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send();
http.Response res = await http.Response.fromStream(response);
print("reached");
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
showSpinner = false;
});
}
Future getImage() async {
final pickedImage =
await ImagePicker().pickImage(source: ImageSource.gallery);
selectedImage = File(pickedImage!.path);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
selectedImage == null
? Text("please pick a image to upload")
: Image.file(selectedImage!),
TextButton.icon(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromARGB(255, 62, 187, 0))),
onPressed: uploadImage,
icon: Icon(Icons.upload_file_rounded, color: Colors.black38),
label: Text("Upload waste sample Image",
style: TextStyle(color: Colors.black)))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
child: Icon(Icons.add_a_photo),
),
);
}
}
I am learning how to use the SharedPreferences library in Flutter.
I created this code and I would like the counter and counter2 variables once I close and reopen the app to remain as the last save.
However, when I reopen the app the counter and counter2 values return to 0.
Can anyone explain to me where I am going wrong?
Thank you.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'data.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: 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({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int counter = 0;
int counter2 = 0;
increment() {
setState(() {
counter += 1;
counter2 += 2;
});
}
loadData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
String? json = prefs.getString('UserData');
print('loaded json: $json');
if (json == null) {
print('NO DATA (null)');
} else {
Map<String, dynamic> map = jsonDecode(json);
print('map $map');
final data = Data.fromJson(map);
print('Data ${data.counter}, ${data.counter2}');
}
});
}
saveData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final _data = Data(counter: counter, counter2: counter2);
String json = jsonEncode(_data);
print('saved json: $json');
prefs.setString('UserData', json);
}
clearData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.clear();
print('data cleared');
}
/// dichiarare l' initState()
#override
void initState() {
super.initState();
loadData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'You have pushed the button this many times:',
),
Text(
'c: $counter, c2: $counter2',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
increment();
saveData();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class Data {
int counter = 0;
int counter2 = 0;
Data({required this.counter, required this.counter2});
Map<String, dynamic> toJson() {
return {
'counter': counter,
'counter2': counter2,
};
}
Data.fromJson(Map<String, dynamic> json) {
counter = json['counter'];
counter2 = json['counter2'];
}
}
I agree with the other answer, the best is to use a FutureBuilder. But you can make your current code work with simply adding two lines at the end of loadData:
loadData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
String? json = prefs.getString('UserData');
print('loaded json: $json');
if (json == null) {
print('NO DATA (null)');
} else {
Map<String, dynamic> map = jsonDecode(json);
print('map $map');
final data = Data.fromJson(map);
print('Data ${data.counter}, ${data.counter2}');
// add these lines
counter = data.counter;
counter2 = data.counter2;
}
});
}
What happens (as the other answer says) is that your widget is first built without knowing the values from SharedPreferences. After a little time this first build is done, the loadData future completes, and with setState the widget is rebuilt.
In a real application you'd like to avoid unnecessary builds, so you'd rather display a progress indicator while async data is being loaded, check FutureBuilder.
A short answer is that when you call loadData(); inside initState the function is performed asynchronously relative to the rest of the widget, so your Scaffold is built before the data is available. This is why you are seeing the data in from your print but not in the app.
One way to address it is to us a https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
I'm trying in Android to show a local notification as a test, everything works fine if the app is foreground and background, but I need the notification to show when the app is terminated, which is this mode (when I terminate the app) workmanager doesn't work .
I have seen many questions and answers but I have not found anything clear, does anyone know how to make the notification appear when the application is finished? I don't want to use push notifications or Fcm, I know that, but I need to know how to do this locally.
my main.dart
I'm trying to show a local notification as a test, everything works fine if the app is foreground and background, but I need the notification to show when the app is terminated, which is this mode (when I terminate the app) workmanager doesn't work .
I have seen many questions and answers but I have not found anything clear, does anyone know how to make the notification appear when the application is finished? I don't want to use push notifications or Fmc, I know that, but I need to know how to do this locally.
my main.dart
import 'package:flutter/material.dart';
import 'package:workmanager/workmanager.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
void callbackDispatcher() {
Workmanager.executeTask((task, inputData) async {
if (task == 'uniqueKey') {
///do the task in Backend for how and when to send notification
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails('your channel id', 'your channel name',
'your channel description',
importance: Importance.max,
priority: Priority.high,
showWhen: false);
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0,
'hello',
'1245',
platformChannelSpecifics,
payload: 'item x');
}
return Future.value(true);
});
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
Workmanager.initialize(
callbackDispatcher,
isInDebugMode:
true);
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings(
'#mipmap/ic_launcher');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings();
final MacOSInitializationSettings initializationSettingsMacOS =
MacOSInitializationSettings();
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
macOS: initializationSettingsMacOS);
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: selectNotification);
runApp(const MyApp());
}
Future selectNotification(String payload) async {
if (payload != null) {
debugPrint('notification payload: $payload');
}
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text("Work manager Example"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
Workmanager.registerPeriodicTask(
"1",
"uniqueKey",
frequency: Duration(minutes: 15),
initialDelay: Duration(seconds:10),
);
print('START');
},
child: Text("Run Task")),
SizedBox(
height: 10,
),
ElevatedButton(
onPressed: () {
Workmanager.cancelByUniqueName("1");
print('cancel');
},
child: Text("Cancel Task"))
],
),
),
);
}
#override
void initState() {
// TODO: implement initState
super.initState();
}
}
I have searched for similar questions and answers to this question but haven't found any specific answer till now.
I am trying to save downloaded files to my internal phone storage. Preferably the download folder... Am using d i o and path provider.
Have tried using "get External Storage Directory". But even after the download I can't locate the file anywhere in my device.
Please how do I specify the download path to something like /storage/emulated/0/Download
You can copy paste run full code below
This example code download a pdf file with Dio and save to Downloads directory
Step 1: downloads_path_provider has archived by the owner, you can use package https://pub.dev/packages/ext_storage
code snippet
String path = await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOWNLOADS);
print(path);
Step 2: Add permission in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Step 3: pubspec.yaml , notice permission_handler is 4.4.0
dependencies:
flutter:
sdk: flutter
dio: any
permission_handler: 4.4.0
ext_storage: any
Step 4: Dio for download file
Future download2(Dio dio, String url, String savePath) async {
try {
Response response = await dio.get(
url,
onReceiveProgress: showDownloadProgress,
//Received data with List<int>
options: Options(
responseType: ResponseType.bytes,
followRedirects: false,
validateStatus: (status) {
return status < 500;
}),
);
print(response.headers);
File file = File(savePath);
var raf = file.openSync(mode: FileMode.write);
// response.data is List<int> type
raf.writeFromSync(response.data);
await raf.close();
} catch (e) {
print(e);
}
}
output
I/flutter (13605): full path /storage/emulated/0/Download/test.pdf
I/flutter (13605): 62%
I/flutter (13605): 100%
full code
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:ext_storage/ext_storage.dart';
import 'dart:io';
import 'package:permission_handler/permission_handler.dart';
final imgUrl =
"https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
var dio = Dio();
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, 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 getPermission() async {
print("getPermission");
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler().requestPermissions([PermissionGroup.storage]);
}
#override
void initState() {
getPermission();
super.initState();
}
Future download2(Dio dio, String url, String savePath) async {
try {
Response response = await dio.get(
url,
onReceiveProgress: showDownloadProgress,
//Received data with List<int>
options: Options(
responseType: ResponseType.bytes,
followRedirects: false,
validateStatus: (status) {
return status < 500;
}),
);
print(response.headers);
File file = File(savePath);
var raf = file.openSync(mode: FileMode.write);
// response.data is List<int> type
raf.writeFromSync(response.data);
await raf.close();
} catch (e) {
print(e);
}
}
void showDownloadProgress(received, total) {
if (total != -1) {
print((received / total * 100).toStringAsFixed(0) + "%");
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton.icon(
onPressed: () async {
String path =
await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOWNLOADS);
//String fullPath = tempDir.path + "/boo2.pdf'";
String fullPath = "$path/test.pdf";
print('full path ${fullPath}');
download2(dio, imgUrl, fullPath);
},
icon: Icon(
Icons.file_download,
color: Colors.white,
),
color: Colors.green,
textColor: Colors.white,
label: Text('Dowload')),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I haven't tested it, but if you want to save your downloaded files to the download directory, there is a package for that : downloads_path_provider
If you encounter issues, you can use the path_provider package instead.
It gives you access to several directories of devices.
You can use "flutter_file_dialog" to handle the issue. After succesfully saving file into app you can provide the path of file to this package and save it in another folder of phone. You can use the function below; ** fromDest ** is the path of the saved file.
_saveFileToDest(String fromDest) async {
final params =
SaveFileDialogParams(sourceFilePath: fromDest);
final filePath = await FlutterFileDialog.saveFile(params: params);
if (kDebugMode) {
print(filePath);
}
}