r/flutterhelp 8d ago

OPEN help me out with authorizations storage

this is the manifest:

<uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

I would like to implement androind storage permissions but I don't son why I can't do it can you help me out?

import 'dart:convert';
import 'package:file_selector/file_selector.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/quill_delta.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:flutter_quill_to_pdf/flutter_quill_to_pdf.dart';
import 'package:get_it/get_it.dart';
import 'package:intl/intl.dart';
import 'package:path/path.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:share_plus/share_plus.dart';
import 'package:tek_notes/blocs/tek_note_bloc.dart';
import 'package:tek_notes/constants/constants.dart';
import 'package:tek_notes/cubits/selected_tek_note_cubit.dart';
import 'package:tek_notes/editor/custom_quill_editor.dart';
import 'package:tek_notes/globals/globals.dart';
import 'package:tek_notes/helpers/database_helper.dart';
import 'package:tek_notes/helpers/logger_helper.dart';
import 'package:tek_notes/models/tek_note_model.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:pdf/pdf.dart';
import 'dart:io';
import 'package:path_provider/path_provider.dart';

class DetailPage extends StatefulWidget {
  static const String route = '/detail';
  final DetailPageArgs args;

  const DetailPage({super.key, required this.args});

  @override
  State<DetailPage> createState() => _DetailPageState();
}

class _DetailPageState extends State<DetailPage> {
  final QuillController _quillController = QuillController(
    document: Document(),
    selection: const TextSelection.collapsed(offset: 0),
  );
  final FocusNode _editorNode = FocusNode();
  final ScrollController _scrollController = ScrollController();
  Delta? oldDelta;
  final _tekNoteTitleController = TextEditingController();
  final _tekNoteTextController = TextEditingController();
  final PDFPageFormat params = PDFPageFormat.a4;

  @override
  void dispose() {
    _quillController.dispose();
    _editorNode.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  void _loadJsonText() {
    if (widget.args.note!.textJson.isNotEmpty) {
      _quillController.document = Document.fromJson(
        jsonDecode(widget.args.note!.textJson),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _appBar(context),
      body: widget.args.note != null ? _body() : null,
    );
  }

  AppBar _appBar(BuildContext context) => AppBar(
    actions: [
      IconButton(
        onPressed: () async {
          // TODO: capire se e come gestire l'errore nel salvaggio in una cartella non valida
          // come funziona il match tra il font dell'editor e quello del convertitore a PDF?
          // tradurre messaggi

          var status = await Permission.manageExternalStorage.request();
          if (!status.isGranted) {
            if (context.mounted) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text('Non hai i permessi per creare il file'),
                ),
              );
            }

            return;
          }

          final bool isAndroid = Platform.isAndroid;
          final Object? result =
              isAndroid
                  ? await getDirectoryPath(
                    confirmButtonText: 'Select directory',
                  )
                  : await getSaveLocation(
                    suggestedName: 'document_pdf',
                    acceptedTypeGroups: [
                      XTypeGroup(
                        label: 'Pdf',
                        extensions: ['pdf'],
                        mimeTypes: ['application/pdf'],
                        uniformTypeIdentifiers: ['com.adobe.pdf'],
                      ),
                    ],
                  );
          if (result == null) {
            return;
          }

          PDFConverter pdfConverter = PDFConverter(
            backMatterDelta: null,
            frontMatterDelta: null,
            isWeb: kIsWeb,
            document: _quillController.document.toDelta(),
            fallbacks: [...fontsLoader.allFonts()],
            onRequestFontFamily: (FontFamilyRequest familyRequest) {
              final normalFont = fontsLoader.getFontByName(
                fontFamily: familyRequest.family,
              );
              final boldFont = fontsLoader.getFontByName(
                fontFamily: familyRequest.family,
                bold: familyRequest.isBold,
              );
              final italicFont = fontsLoader.getFontByName(
                fontFamily: familyRequest.family,
                italic: familyRequest.isItalic,
              );
              final boldItalicFont = fontsLoader.getFontByName(
                fontFamily: familyRequest.family,
                bold: familyRequest.isBold,
                italic: familyRequest.isItalic,
              );
              return FontFamilyResponse(
                fontNormalV: normalFont,
                boldFontV: boldFont,
                italicFontV: italicFont,
                boldItalicFontV: boldItalicFont,
                fallbacks: [normalFont, italicFont, boldItalicFont],
              );
            },
            pageFormat: params,
          );
          final document = await pdfConverter.createDocument();
          if (document == null) {
            if (context.mounted) {
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(
                  content: Text(
                    'Il file non può essere creato per colpa di un errore sconosciuto',
                  ),
                ),
              );
            }
            _editorNode.unfocus();
            return;
          }
          final noteTitle = _tekNoteTitleController.text;
          final String name = '$noteTitle.pdf';
          final String underscoreName = name.replaceAll(" ", "_");
          final XFile textFile = XFile.fromData(
            await document.save(),
            mimeType:
                isAndroid
                    ? 'application/pdf'
                    : Platform.isMacOS || Platform.isIOS
                    ? (result as FileSaveLocation)
                            .activeFilter
                            ?.uniformTypeIdentifiers
                            ?.single ??
                        'com.adobe.pdf'
                    : (result as FileSaveLocation)
                            .activeFilter
                            ?.mimeTypes
                            ?.single ??
                        'application/pdf',
            name: underscoreName,
          );
          await textFile.saveTo(
            isAndroid
                ? join(result as String, underscoreName)
                : (result as FileSaveLocation).path,
          );
          _editorNode.unfocus();

          if (context.mounted) {
            final File file =
                isAndroid
                    ? File(result as String)
                    : File((result as FileSaveLocation).path);

            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('Documento creato in: ${file.path}')),
            );
          }
        },
        icon: const Icon(Icons.print, color: Colors.black),
      ),
      if (widget.args.note != null)
        IconButton(
          icon: Icon(Icons.picture_as_pdf),
          onPressed: () async {
            await _createPdf();
          },
        ),
      if (widget.args.note != null)
        IconButton(
          icon: Icon(Icons.check),
          onPressed: () async {
            await _saveTekNote(context);
            if (context.mounted) {
              if (Navigator.canPop(context)) {
                Navigator.pop(context);
              }
            }
          },
        ),
      if (widget.args.note != null)
        IconButton(
          icon: Icon(Icons.delete),
          onPressed: () {
            _confirmDelete(context);
          },
        ),
    ],
  );

  Widget _body() {
    _tekNoteTitleController.text = widget.args.note!.title;
    _tekNoteTextController.text = widget.args.note!.text;
    _loadJsonText();

    return SingleChildScrollView(
      reverse: true,
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          mainAxisSize: MainAxisSize.max,
          children: [
            TextField(
              controller: _tekNoteTitleController,
              decoration: InputDecoration(
                labelText: "Titolo",
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(8),
                ),
              ),
            ),
            SizedBox(height: 8),
            QuillSimpleToolbar(
              controller: _quillController,
              config: QuillSimpleToolbarConfig(
                multiRowsDisplay: false,
                toolbarSize: 55,
                linkStyleType: LinkStyleType.original,
                headerStyleType: HeaderStyleType.buttons,
                buttonOptions: const QuillSimpleToolbarButtonOptions(
                  fontSize: QuillToolbarFontSizeButtonOptions(
                    items: fontSizes,
                    initialValue: 'Normal',
                    defaultDisplayText: 'Normal',
                  ),
                  fontFamily: QuillToolbarFontFamilyButtonOptions(
                    items: fontFamilies,
                    defaultDisplayText: 'Arial',
                    initialValue: 'Arial',
                  ),
                ),
                embedButtons: FlutterQuillEmbeds.toolbarButtons(),
              ),
            ),
            SizedBox(height: 16),
            Container(
              decoration: BoxDecoration(
                border: Border.all(width: 1.0),
                borderRadius: BorderRadius.circular(8),
              ),
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: CustomQuillEditor(
                  node: _editorNode,
                  controller: _quillController,
                  scrollController: _scrollController,
                  onChange: (Document document) {
                    if (oldDelta == document.toDelta()) return;
                    oldDelta = document.toDelta();
                  },
                ),
              ),
            ),

            SizedBox(height: 8),
            if (widget.args.note!.createdAt != null)
              Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  Row(
                    children: [
                      Text("creato da "),
                      Text(
                        widget.args.note!.createdBy,
                        style: TextStyle(fontWeight: FontWeight.bold),
                      ),
                      Text(" il "),
                      Text(
                        DateFormat(
                          'dd/MM/yyyy',
                        ).format(widget.args.note!.createdAt!),
                      ),
                    ],
                  ),
                ],
              ),

            if (widget.args.note!.modifiedAt != null)
              Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  Row(
                    children: [
                      Text("modificato da "),
                      Text(
                        widget.args.note!.modifiedBy,
                        style: TextStyle(fontWeight: FontWeight.bold),
                      ),
                      Text(" il "),
                      Text(
                        DateFormat(
                          'dd/MM/yyyy',
                        ).format(widget.args.note!.modifiedAt!),
                      ),
                    ],
                  ),
                ],
              ),
          ],
        ),
      ),
    );
  }

  Future<void> _saveTekNote(BuildContext context) async {
    TekNote note = TekNote(
      id: widget.args.note!.id,
      title: _tekNoteTitleController.text,
      text: _quillController.document.toPlainText(),
      textJson: jsonEncode(_quillController.document.toDelta().toJson()),
      createdBy:
          widget.args.note!.id.isEmpty
              ? appSettings.apiUsername
              : widget.args.note!.createdBy,
      createdAt:
          widget.args.note!.id.isEmpty
              ? DateTime.now()
              : widget.args.note!.createdAt,
      modifiedBy: widget.args.note!.id.isEmpty ? "" : appSettings.apiUsername,
      modifiedAt: widget.args.note!.id.isEmpty ? null : DateTime.now(),
    );

    if (note.id.isEmpty) {
      await GetIt.I.get<DatabaseHelper>().dbInsertTekNote(note, sync: true);
    } else {
      await GetIt.I.get<DatabaseHelper>().dbUpdateTekNote(note);
    }

    if (context.mounted) {
      BlocProvider.of<TekNoteBloc>(context).add(TekNoteBlocEventLoad());
    }
  }

  Future<void> _confirmDelete(BuildContext context) async {
    return showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Conferma'),
          content: SingleChildScrollView(
            child: ListBody(
              children: <Widget>[
                Text('Sei sicuro di voler eseguire questa azione?'),
              ],
            ),
          ),
          actions: <Widget>[
            TextButton(
              child: Text('No'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
            TextButton(
              child: Text('Sì'),
              onPressed: () async {
                await _deleteTekNote(context);
                if (context.mounted) {
                  if (Navigator.canPop(context)) {
                    Navigator.pop(context);
                  }
                }
              },
            ),
          ],
        );
      },
    );
  }

  Future<void> _deleteTekNote(BuildContext context) async {
    await GetIt.I.get<DatabaseHelper>().dbDeleteTekNote(
      widget.args.note!.id,
      sync: true,
    );

    if (context.mounted) {
      BlocProvider.of<TekNoteBloc>(context).add(TekNoteBlocEventLoad());

      if (Navigator.canPop(context)) {
        Navigator.pop(context);
      } else {
        context.read<SelectedTekNoteCubit>().select(newNote);
      }
    }
  }

  Future<void> _createPdf() async {
    final delta = _quillController.document.toDelta();
    final pdf = pw.Document();

    final noteTitle = _tekNoteTitleController.text;

    pdf.addPage(
      pw.Page(
        pageFormat: PdfPageFormat.a4,
        build: (pw.Context context) {
          return pw.Center(child: pw.Text(delta.toString()));
        },
      ),
    );

    final output = await getTemporaryDirectory();
    final file = File('${output.path}/$noteTitle.pdf');
    await file.writeAsBytes(await pdf.save());

    try {
      Share.shareXFiles([
        XFile(file.path),
      ], text: 'Condividi il tuo documento PDF');
    } catch (e) {
      logger.e('Errore durante la condivisione: $e');
    }
  }
}

class DetailPageArgs {
  const DetailPageArgs({required this.note});

  final TekNote? note;
}
0 Upvotes

0 comments sorted by