Displaying a created PDF in a new tab for flutter web - flutter

I am currently creating a PDF to display inputted data from the user and I want to display the pdf in a new tab so they can either print it or download it or just close out.
The current PDF creation method is:
Future<void> createPDF() async{
final PdfDocument document = PdfDocument();
document.pages.add().graphics.drawString(
'Hello World!', PdfStandardFont(PdfFontFamily.helvetica, 20),
brush: PdfSolidBrush(PdfColor(0, 0, 0)),
bounds: Rect.fromLTWH(20, 60, 150, 30));
List<int> bytes = await document.save();
document.dispose();
saveAndLaunchFile(bytes, '$NewConfig.pdf');
}
then to open the file I have this method:
Future<void> saveAndLaunchFile(List<int> bytes, String fileName) async {
Uint8List test = Uint8List.fromList(bytes);
final pdfController = PdfController(document:PdfDocument.openData(test));
PdfView(controller: pdfController);
}
The issue I am running into is the new tab that opens displays this:
37806870454946551310371311462502541310493248321119810613106060131047841211121013247679711697108111103131047809710310111532503248328213106262131010111010011198106131050324832111981061310606013104784121112101324780971031011151310477510510011532915132483282931310476711111711011632491310478210111511111711499101115326060626213101310477710110010597661111203291483248325357533256525093131062621310101110100111981061310513248321119810613106060131047671111171101163249131047841211121013247809710310111513104775105100115329152324832829313104780971141011101163250324832821310626213101011101001119810613105232483211198106131060601310478412111210132478097103101131047809711410111011632513248328213104767111110116101110116115329153324832823254324832823255324832829313104782101115111117114991011153260601310478011411199831011163291478068703247841011201169313104770111110116326060131047994998565353545345534851514552101565345985399504553565154485748981015048513256324832821310626213101310626213101310626213101011101001119810613105332483211198106131060601310477010510811610111432477010897116101681019911110010113104776101110103116104325713106262131011511611410197109131012094434001140114131010111010011511611410197109131010111010011198106131054324832111981061310606013104770105108116101114324770108971161016810199111100101131047761011101031161043257131062621310115116114101971091310120941140082082131010111010011511611410197109131010111010011198106131055324832111981061310606013104770105108116101114324770108971161016810199111100101131047761011101031161043250505713106262131011511611410197109131012094101143657534916133239129252135241322146718217921720554949169175213642071871051869320038539138254122147698014597301321532472302034337215176592471105216061243225561856225136314915325146710778105157252165162164620485841822824418812023711612918515227228989410517881108111961551426748148156415712843203422444621814625525521417525223914411496171408922315518373155231199452369425460117164423832424364215131201621976962111762147219361615719178233240147195177114221341688311317017616894244613148201231158207322789229127168204178122502141225624896143871835514860100184613795972178011310101110100115116114101971091310101110100111981061310563248321119810613106060131047841211121013247701111101161310478311798116121112101324784121112101491310476697115101701111101163247721011081181011161059997131047691109911110010511010332478710511065110115105691109911110010511010313106262131010111010011198106131012011410110213104832571310484848484848484848483254535351533210213104848484848484848495532484848484832110131048484848484848485550324848484848321101310484848484848484956483248484848483211013104848484848484850535732484848484832110131048484848484848525256324848484848321101310484848484848485351543248484848483211013104848484848484854505232484848484832110131048484848484848575152324848484848321101310116114971051081011141310606013104782111111116324932483282131047831051221013257131062621310131011511697114116120114101102131049485157131037376979701310
which I am guessing is the bytes but am at a loss as to what I am supposed to do from here.
any info?

I suggest you to directly use a pub package like native_pdf_view.
Its easyer than your method in my opinion and you dont need to reinvente the wheel.
EDIT:
Here is a code that i do quickly, it can be improve with a ChangeNotifier etc... to handle Future function, But it work.
main.dart
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:native_pdf_view/native_pdf_view.dart';
import 'package:syncfusion_flutter_pdf/pdf.dart' as scPdf;
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 PdfController _pdfController;
bool isPdfLoaded = false;
Future<void> createPDF() async{
final scPdf.PdfDocument document = scPdf.PdfDocument();
document.pages.add().graphics.drawString(
'Hello World!', scPdf.PdfStandardFont(scPdf.PdfFontFamily.helvetica, 20),
brush: scPdf.PdfSolidBrush(scPdf.PdfColor(0, 0, 0)),
bounds: Rect.fromLTWH(20, 60, 150, 30));
List<int> bytes = await document.save();
document.dispose();
saveAndLaunchFile(bytes, 'test.pdf');
}
Future<void> saveAndLaunchFile(List<int> bytes, String fileName) async {
Uint8List test = Uint8List.fromList(bytes);
final pdfController = PdfController(document:PdfDocument.openData(test));
setState(() {
isPdfLoaded = true;
_pdfController = pdfController;
});
}
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'PDF',
),
isPdfLoaded ? Container(width : size.width, height: size.height * 0.5, child : PdfView(controller: _pdfController)) : Text("Pdf not loaded"),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: createPDF,
tooltip: 'CreatePDF',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Re-EDIT:
This is the exemple using Navigator.push to display the pdf in another page:
main.dart
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:native_pdf_view/native_pdf_view.dart';
import 'package:stackoverflow/pdf_view.dart';
import 'package:syncfusion_flutter_pdf/pdf.dart' as scPdf;
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 PdfController _pdfController;
bool isPdfLoaded = false;
Future<void> createPDF() async{
final scPdf.PdfDocument document = scPdf.PdfDocument();
document.pages.add().graphics.drawString(
'Hello World!', scPdf.PdfStandardFont(scPdf.PdfFontFamily.helvetica, 20),
brush: scPdf.PdfSolidBrush(scPdf.PdfColor(0, 0, 0)),
bounds: Rect.fromLTWH(20, 60, 150, 30));
List<int> bytes = await document.save();
document.dispose();
saveAndLaunchFile(bytes, 'test.pdf');
}
Future<void> saveAndLaunchFile(List<int> bytes, String fileName) async {
Uint8List test = Uint8List.fromList(bytes);
final pdfController = PdfController(document:PdfDocument.openData(test));
await Navigator.of(context).push((MaterialPageRoute(builder: (context) => PdfViewPage(pdfController: pdfController))));
}
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Click on Floating button to create and open your pdf in another view',
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: createPDF,
tooltip: 'CreatePDF',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
pdf_view.dart
import 'package:flutter/material.dart';
import 'package:native_pdf_view/native_pdf_view.dart';
class PdfViewPage extends StatelessWidget {
const PdfViewPage({Key? key, required this.pdfController}) : super(key: key);
final PdfController pdfController;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: PdfView(controller: pdfController)
),
);
}
}

Related

Flutter Google Oauth2 signIn how to get _idToken

I am currently working on flutter app and I need to integrate google login. I am using google cloud Oauth and so far I integrated google login. But the issue is in the success response from google the _idtoken is null. Why I don't get the _idtoken
I need the _idToken to authenticate user from backend. What should I do
Below is my main.dart file
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_application_2/api/google_signin_api.dart';
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(
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() {
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,
),
ElevatedButton(onPressed: signIn, child: const Text("Click"))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Future signIn() async {
final user = await GoogleSignInApi.login();
//print(user.idtoken)
inspect(user);
}
}
Below is google_signin_api.dart file
import 'package:google_sign_in/google_sign_in.dart';
class GoogleSignInApi {
static final _googleSignIn = GoogleSignIn();
static Future<GoogleSignInAccount?> login() async {
return _googleSignIn.signIn();
}
}
this is the success response
enter image description here

Flutter - How to call an API every n minutes?

I need to call an API every n minutes. The data should be available across all screens. How can I implement this at app level. I am not using any state management tools.
void main() {
periodicSub = Stream.periodic(const Duration(seconds: 10))
.listen((_) {
///fetch data
someFuture =
Future<List<someObject>>.delayed(
const Duration(milliseconds: 500), () async {
return someFunction();
});
});
someFuntions returns a list. I want a certain FutureBuilder on HomePage to execute whenever the list is updated.
Here is an example using "https://pub.dev/packages/provider"
First create a Notifier:
import 'dart:async';
import 'package:flutter/material.dart';
class CustomNotifier with ChangeNotifier {
int counter = 0;
CustomNotifier() {
Stream.periodic(const Duration(seconds: 10)).listen((_) {
///fetch data
Future<List<dynamic>>.delayed(const Duration(milliseconds: 500),
() async {
return someFunction();
});
});
}
someFunction() {
counter++;
notifyListeners();
}
}
Then you could use it like:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'notifier.dart';
void main() {
final customNotifier = CustomNotifier();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => customNotifier,
),
//You could add more providers
],
builder: (context, _) {
return const MyApp();
},
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#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> {
#override
Widget build(BuildContext context) {
var customNotifier = Provider.of<CustomNotifier>(
context,
);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'someFunction runs this many times:',
),
Text(
'${customNotifier.counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
);
}
}

Why "StateNotifierProvider + Consumer" is not rebuilding the widget?

This is de main.dart file of a flutter app using riverpod_flutter package.
Starting at 3, the counter is increasing its value by 2 on each click, but the screen doesn't refresh (it shows 3 while the console prints: 5, 7, 9, etc...).
This is the code for "StateNotifierProvider + Consumer", but I get the same result when using ConsumerWidget instead of Consumer.
Any suggestion?
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateNotifierProvider<Counter, int>((ref) => Counter());
class Counter extends StateNotifier<int> {
Counter() : super(3);
String toString() => state.toString();
void increment() => state++;
void decrement() => state--;
}
void main() {
runApp(ProviderScope(
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'RiverPod Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('RiverPod example'),
),
body: Container(
child: Center(
child: Consumer(builder: (context, watch, child) {
Counter data = context.read(counterProvider.notifier);
Counter data2 = watch(counterProvider.notifier);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(data.toString()),
Text(data2.toString()),
SizedBox(
height: 10,
),
FloatingActionButton(
onPressed: () {
context.read(counterProvider.notifier).increment();
data2.increment();
print(data.toString());
},
child: const Icon(Icons.add),
)
],
);
}),
),
),
);
}
}
Thank you.
With watch(counterProvider.notifier), you are not actually watching the state, but you are watching the notifier itself (that is why you can call increment). You should not use watch to get the notifier, but only context.read (as you did with the data property).
To rebuild when the state changes, you have to watch the actual state: watch(counterProvider);. This will return the integer state, which you can then use to display the current value.
You also should not get the state directly from the notifier (your toString) method. The StateNotifier should be used to manage te state, but the actual state itself should be used to render the Widgets.
Change 1:
Instead of this:
Counter data = context.read(counterProvider.notifier);
Counter data2 = watch(counterProvider.notifier);
Use this:
final int data = context.read(counterProvider);
final int data2 = watch(counterProvider);
Change 2:
Instead of this:
context.read(counterProvider.notifier).increment();
data2.increment();
Use this:
context.read(counterProvider.notifier).increment();
watch(counterProvider.notifier).increment();
Change 3 (optional, no needed to make it work):
Comment this line:
//String toString() => state.toString();
Change 4 (optional, no needed to make it work):
Instead of this:
final counterProvider = StateNotifierProvider<Counter, int>((ref) => Counter());
Use this:
final counterProvider = StateNotifierProvider((ref) => Counter());
But if you do, you need to change also this:
final int data = context.read(counterProvider);
final int data2 = watch(counterProvider);
For this:
final int data = (context.read(counterProvider) as int);
final int data2 = (watch(counterProvider) as int);
Or this:
final data = context.read(counterProvider);
final data2 = watch(counterProvider);
This is the final code (one version):
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateNotifierProvider((ref) => Counter());
class Counter extends StateNotifier<int> {
Counter() : super(3);
//String toString() => state.toString();
void increment() => state++;
void decrement() => state--;
}
void main() {
runApp(ProviderScope(
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'RiverPod Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('RiverPod example'),
),
body: Container(
child: Center(
child: Consumer(builder: (context, watch, child) {
//Counter data = context.read(counterProvider.notifier);
//Counter data2 = watch(counterProvider.notifier);
final int data = (context.read(counterProvider) as int);
final int data2 = (watch(counterProvider) as int);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(data.toString()),
Text(data2.toString()),
SizedBox(
height: 10,
),
FloatingActionButton(
onPressed: () {
context.read(counterProvider.notifier).increment();
watch(counterProvider.notifier).increment();
print(data.toString());
},
child: const Icon(Icons.add),
)
],
);
}),
),
),
);
}
}

ImagePickerWeb Output is File$ instead of File

I am using ImagePickerWeb to allow users to upload photos from my app
Future<void> getPhotos() async {
var imageFile = await ImagePickerWeb.getImage(outputType: ImageType.file);
print(imageFile);
if (imageFile != null) {
setState(() {
currentSelfie = imageFile;
_accDetails['customer_selfie'] = currentSelfie;
});
}
When I try to display the image via Image.File
Image.file(
currentSelfie,
height: screenAwareSize(100, context),
width: screenAwareSize(100, context),
fit: BoxFit.fill,
),
I get this error
File$ ([object File]) :<getObject: NoSuchMethodError: The
getter 'uri' was called on null.>
I am using the file format for my because I am passing the image to my back end server and it receives the data as a file. Any help would be appreciated.
You can copy paste run full code below
According to owner's description https://github.com/Ahmadre/image_picker_web#how-do-i-get-all-informations-out-of-my-imagevideo-eg-image-and-file-in-one-run
You can use ImagePickerWeb.getImageInfo and show image with Image.memory
code snippet
Future<void> getPhotos() async {
var mediaData = await ImagePickerWeb.getImageInfo;
String mimeType = mime(Path.basename(mediaData.fileName));
html.File mediaFile =
new html.File(mediaData.data, mediaData.fileName, {'type': mimeType});
print("imageFile ${mediaData.toString()}");
if (mediaData != null) {
currentSelfie = mediaData.data;
setState(() {});
}
}
...
currentSelfie == null
? Container()
: Image.memory(
(currentSelfie),
fit: BoxFit.fill,
),
working demo
full code
import 'dart:async';
import 'dart:io';
import 'package:mime_type/mime_type.dart';
import 'package:path/path.dart' as Path;
import 'package:flutter/material.dart';
import 'package:image_picker_web/image_picker_web.dart';
import 'dart:html' as html;
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> {
var currentSelfie;
Future<void> getPhotos() async {
var mediaData = await ImagePickerWeb.getImageInfo;
String mimeType = mime(Path.basename(mediaData.fileName));
html.File mediaFile =
new html.File(mediaData.data, mediaData.fileName, {'type': mimeType});
print("imageFile ${mediaData.toString()}");
if (mediaData != null) {
currentSelfie = mediaData.data;
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
currentSelfie == null
? Container()
: Image.memory(
(currentSelfie),
fit: BoxFit.fill,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: getPhotos,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

get local time for different location in flutter

is there a simple way to get time for a different location than the current location in flutter ?
for example the current location is set to Japan,Tokyo and I want the time in Turkey, Istanbul from the system itself not from API
You can copy paste run full code below
You can use package https://pub.dev/packages/timezone
Step 1: download 2019c.tzf from https://github.com/srawlins/timezone/tree/master/lib/data
Step 2: put 2019c.tzf to assets directory
Step 3: Edit pubspec.yaml
working demo
code snippet
void main() async {
WidgetsFlutterBinding.ensureInitialized();
var byteData = await rootBundle.load('assets/2019c.tzf');
initializeDatabase(byteData.buffer.asUint8List());
runApp(MyApp());
}
...
final detroit = getLocation('America/Detroit');
final us = getLocation('US/Pacific');
final tokyo = getLocation('Asia/Tokyo');
nowDetroit = new TZDateTime.now(detroit);
nowUs = new TZDateTime.now(us);
nowTokyo = TZDateTime.now(tokyo);
full code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:timezone/timezone.dart';
import 'package:timezone/standalone.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
var byteData = await rootBundle.load('assets/2019c.tzf');
initializeDatabase(byteData.buffer.asUint8List());
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;
TZDateTime nowDetroit;
TZDateTime nowUs;
TZDateTime nowTokyo;
void _incrementCounter() {
final detroit = getLocation('America/Detroit');
final us = getLocation('US/Pacific');
final tokyo = getLocation('Asia/Tokyo');
nowDetroit = new TZDateTime.now(detroit);
nowUs = new TZDateTime.now(us);
nowTokyo = TZDateTime.now(tokyo);
_counter++;
setState(() {
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(' America/Detroit ${nowDetroit.toString()}'),
Text(' US/Pacific ${nowUs.toString()}'),
Text(' Asia/Tokyo ${nowTokyo.toString()}'),
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),
),
);
}
}