Flutter Directory.systemTemp не работает в выпуске apk

Я пытаюсь захватить изображение с камеры и сохранить его в кеше (например, Directory.systemTemp, доступном в пакете dart.io). Он отлично работает в режиме отладки. Но когда я собираю релизный apk и устанавливаю, он не работает. Это код:

import 'dart:async';
import 'dart:io';

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';

class CameraExampleHome extends StatefulWidget {
  @override
  _CameraExampleHomeState createState() {
    return new _CameraExampleHomeState();
  }
}

/// Returns a suitable camera icon for [direction].
IconData getCameraLensIcon(CameraLensDirection direction) {
  switch (direction) {
    case CameraLensDirection.back:
      return Icons.camera_rear;
    case CameraLensDirection.front:
      return Icons.camera_front;
    case CameraLensDirection.external:
      return Icons.camera;
  }
  throw new ArgumentError('Unknown lens direction');
}

void logError(String code, String message) =>
    print('Error: $code\nError Message: $message');

class _CameraExampleHomeState extends State<CameraExampleHome> {
  CameraController controller;
  String imagePath;

  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  Future<List<CameraDescription>> getCameras() async {
    return await availableCameras();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      key: _scaffoldKey,
      appBar: new AppBar(
        title: const Text('Camera example'),
      ),
      body: FutureBuilder(
        future: getCameras(),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.none:

            case ConnectionState.waiting:
              return new Text('loading...');

            default:
              if (snapshot.hasError)
                return new Text('Error: ${snapshot.error}');
              else {
                if (snapshot.hasData)
                  return createCameraView(context, snapshot);
                else
                  return new Text('Null');
              }
          }
        },
      ),
    );
  }

  /// Display the preview from the camera (or a message if the preview is not available).
  Widget _cameraPreviewWidget() {
    if (controller == null || !controller.value.isInitialized) {
      return const Text(
        'Tap a camera',
        style: const TextStyle(
          color: Colors.white,
          fontSize: 24.0,
          fontWeight: FontWeight.w900,
        ),
      );
    } else {
      return new AspectRatio(
        aspectRatio: controller.value.aspectRatio,
        child: new CameraPreview(controller),
      );
    }
  }

  /// Display the thumbnail of the captured image or video

  /// Display the control bar with buttons to take pictures and record videos.
  Widget _captureControlRowWidget() {
    return new Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
        new IconButton(
          icon: const Icon(Icons.camera_alt),
          color: Colors.blue,
          onPressed: controller != null &&
                  controller.value.isInitialized &&
                  !controller.value.isRecordingVideo
              ? onTakePictureButtonPressed
              : null,
        ),
      ],
    );
  }

  /// Display a row of toggle to select the camera (or a message if no camera is available).
  Widget _cameraTogglesRowWidget(var cameras) {
    final List<Widget> toggles = <Widget>[];

    if (cameras.isEmpty) {
      return const Text('No camera found');
    } else {
      for (CameraDescription cameraDescription in cameras) {
        toggles.add(
          new SizedBox(
            width: 90.0,
            child: new RadioListTile<CameraDescription>(
              title:
                  new Icon(getCameraLensIcon(cameraDescription.lensDirection)),
              groupValue: controller?.description,
              value: cameraDescription,
              onChanged: controller != null && controller.value.isRecordingVideo
                  ? null
                  : onNewCameraSelected,
            ),
          ),
        );
      }
    }

    return new Row(children: toggles);
  }

  String timestamp() => new DateTime.now().millisecondsSinceEpoch.toString();

  void showInSnackBar(String message) {
    _scaffoldKey.currentState
        .showSnackBar(new SnackBar(content: new Text(message)));
  }

  void onNewCameraSelected(CameraDescription cameraDescription) async {
    if (controller != null) {
      await controller.dispose();
    }
    controller = new CameraController(cameraDescription, ResolutionPreset.high);

    // If the controller is updated then update the UI.
    controller.addListener(() {
      if (mounted) setState(() {});
      if (controller.value.hasError) {
        showInSnackBar('Camera error ${controller.value.errorDescription}');
      }
    });

    try {
      await controller.initialize();
    } on CameraException catch (e) {
      _showCameraException(e);
    }

    if (mounted) {
      setState(() {});
    }
  }

  void onTakePictureButtonPressed() {
    takePicture().then((String filePath) {
      if (mounted) {
        setState(() {
          imagePath = filePath;
          // videoController?.dispose();
          // videoController = null;
        });
        if (filePath != null) showInSnackBar('Picture saved to $filePath');
      }
    });
  }

  Future<String> takePicture() async {
    if (!controller.value.isInitialized) {
      showInSnackBar('Error: select a camera first.');
      return null;
    }
    //final Directory extDir = await getApplicationDocumentsDirectory();
    final Directory extDir = Directory.systemTemp;
    final String dirPath = '${extDir.path}/Pictures/flutter_test';
    await new Directory(dirPath).create(recursive: true);
    final String filePath = '$dirPath/${timestamp()}.jpg';

    if (controller.value.isTakingPicture) {
      // A capture is already pending, do nothing.
      return null;
    }

    try {
      await controller.takePicture(filePath);
    } on CameraException catch (e) {
      _showCameraException(e);
      return null;
    }
    return filePath;
  }

  void _showCameraException(CameraException e) {
    logError(e.code, e.description);
    showInSnackBar('Error: ${e.code}\n${e.description}');
  }

  Widget createCameraView(BuildContext context, AsyncSnapshot snapshot) {
    return new Column(
      children: <Widget>[
        new Expanded(
          child: new Container(
            width: MediaQuery.of(context).size.width,
            child: _cameraPreviewWidget(),
          ),
        ),
        _captureControlRowWidget(),
        new Padding(
          padding: const EdgeInsets.all(5.0),
          child: new Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              _cameraTogglesRowWidget(snapshot.data),
              //_thumbnailWidget(),
            ],
          ),
        ),
      ],
    );
  }
}

class CameraApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new CameraExampleHome(),
    );
  }
}

List<CameraDescription> cameras;

void main() {
  // Fetch the available cameras before initializing the app.

  runApp(new CameraApp());
}

Я попытался настроить каталог на ожидание getApplicationDocumentsDirectory () следующим образом:

final Directory extDir = await getApplicationDocumentsDirectory();
//final Directory extDir = Directory.systemTemp;
final String dirPath = '${extDir.path}/Pictures/flutter_test';
await new Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.jpg';

Потом картинки сохраняются как в режиме отладки, так и в релизном apk. Но изображения теряются, когда я перезапускаю приложение. Я хочу, чтобы они были доступны даже после перезапуска приложения (это основная причина, по которой я хочу использовать кеш).

Так что же происходит не так, когда я пытаюсь сохранить в кеш в сборке релиза приложения?


person Prasanth Kanna    schedule 11.06.2018    source источник


Ответы (1)


вместо этого используйте пакет path_provider, чтобы получить временный путь https://pub.dartlang.org/packages/path_provider < / а>

Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;

Directory.systemTemp предполагается использовать только на сервере / консоли.

person Günter Zöchbauer    schedule 11.06.2018
comment
Как получить путь к кешу с помощью path_provider? - person Prasanth Kanna; 11.06.2018
comment
Каталог кеша, похоже, не закрыт. Я начал поиск в github.com/ flutter / flutter / blob / master / packages / flutter / lib / src /, но не нашел кода, который обращается к каталогу кеша. - person Günter Zöchbauer; 12.06.2018
comment
Чувак await getTemporaryDirectory () из path_provider ссылается на кеш - person Prasanth Kanna; 12.06.2018
comment
вот как это сделать: final pathTemp = await getTemporaryDirectory(); затем myFiles = pathTemp.listSync(); - person Constantine Kurbatov; 22.05.2021