mirror of
https://github.com/sony/flutter-elinux-plugins.git
synced 2025-08-26 11:33:10 +08:00
[camera] update for camera ^0.10.0+1 / flutter 3.3.0 release (#57)
* Update for camera ^0.10.0+1 / flutter 3.3.0 release Signed-off-by: Hidenori Matsubayashi <hidenori.matsubayashi@gmail.com>
This commit is contained in:

committed by
GitHub

parent
02260a1eab
commit
1dd2b8bbfa
@ -1,3 +1,6 @@
|
|||||||
|
## 0.2.1
|
||||||
|
* Update for camera v0.10.0+1 / flutter 3.3.0 release
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
* Fix wrong orientation issue.
|
* Fix wrong orientation issue.
|
||||||
* Change the camera type from back to external.
|
* Change the camera type from back to external.
|
||||||
|
@ -19,7 +19,7 @@ $ sudo apt install libgstreamer-plugins-base1.0-dev \
|
|||||||
### pubspec.yaml
|
### pubspec.yaml
|
||||||
```yaml
|
```yaml
|
||||||
dependencies:
|
dependencies:
|
||||||
camera: ^0.8.1+7
|
camera: ^0.10.0+1
|
||||||
camera_elinux:
|
camera_elinux:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/sony/flutter-elinux-plugins.git
|
url: https://github.com/sony/flutter-elinux-plugins.git
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.15)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
# stop cmake from taking make from CMAKE_SYSROOT
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
project(runner LANGUAGES CXX)
|
project(runner LANGUAGES CXX)
|
||||||
|
|
||||||
set(BINARY_NAME "camera_example")
|
set(BINARY_NAME "camera_example")
|
||||||
@ -7,21 +9,12 @@ cmake_policy(SET CMP0063 NEW)
|
|||||||
|
|
||||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
|
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
|
||||||
|
|
||||||
# Root filesystem for cross-building.
|
# Basically we use this include when we got the following error:
|
||||||
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
|
# fatal error: 'bits/c++config.h' file not found
|
||||||
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
|
include_directories(SYSTEM ${FLUTTER_SYSTEM_INCLUDE_DIRECTORIES})
|
||||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
|
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
|
||||||
|
|
||||||
# Basically we use this include when we got the following error:
|
|
||||||
# fatal error: 'bits/c++config.h' file not found
|
|
||||||
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
|
|
||||||
include_directories(SYSTEM ${FLUTTER_SYSTEM_INCLUDE_DIRECTORIES})
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Configure build options.
|
# Configure build options.
|
||||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 Sony Corporation. All rights reserved.
|
// Copyright 2022 Sony Corporation. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
@ -13,8 +13,6 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// todo: Supports other types besides int, string.
|
|
||||||
|
|
||||||
namespace commandline {
|
namespace commandline {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -39,30 +37,49 @@ class CommandOptions {
|
|||||||
CommandOptions() = default;
|
CommandOptions() = default;
|
||||||
~CommandOptions() = default;
|
~CommandOptions() = default;
|
||||||
|
|
||||||
void AddWithoutValue(const std::string& name, const std::string& short_name,
|
void AddWithoutValue(const std::string& name,
|
||||||
const std::string& description, bool required) {
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
bool required) {
|
||||||
Add<std::string, ReaderString>(name, short_name, description, "",
|
Add<std::string, ReaderString>(name, short_name, description, "",
|
||||||
ReaderString(), required, false);
|
ReaderString(), required, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddInt(const std::string& name, const std::string& short_name,
|
void AddInt(const std::string& name,
|
||||||
const std::string& description, const int& default_value,
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
const int& default_value,
|
||||||
bool required) {
|
bool required) {
|
||||||
Add<int, ReaderInt>(name, short_name, description, default_value,
|
Add<int, ReaderInt>(name, short_name, description, default_value,
|
||||||
ReaderInt(), required, true);
|
ReaderInt(), required, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddString(const std::string& name, const std::string& short_name,
|
void AddDouble(const std::string& name,
|
||||||
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
const double& default_value,
|
||||||
|
bool required) {
|
||||||
|
Add<double, ReaderDouble>(name, short_name, description, default_value,
|
||||||
|
ReaderDouble(), required, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddString(const std::string& name,
|
||||||
|
const std::string& short_name,
|
||||||
const std::string& description,
|
const std::string& description,
|
||||||
const std::string& default_value, bool required) {
|
const std::string& default_value,
|
||||||
|
bool required) {
|
||||||
Add<std::string, ReaderString>(name, short_name, description, default_value,
|
Add<std::string, ReaderString>(name, short_name, description, default_value,
|
||||||
ReaderString(), required, true);
|
ReaderString(), required, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename F>
|
template <typename T, typename F>
|
||||||
void Add(const std::string& name, const std::string& short_name,
|
void Add(const std::string& name,
|
||||||
const std::string& description, const T default_value,
|
const std::string& short_name,
|
||||||
F reader = F(), bool required = true, bool required_value = true) {
|
const std::string& description,
|
||||||
|
const T default_value,
|
||||||
|
F reader = F(),
|
||||||
|
bool required = true,
|
||||||
|
bool required_value = true) {
|
||||||
if (options_.find(name) != options_.end()) {
|
if (options_.find(name) != options_.end()) {
|
||||||
std::cerr << "Already registered option: " << name << std::endl;
|
std::cerr << "Already registered option: " << name << std::endl;
|
||||||
return;
|
return;
|
||||||
@ -213,7 +230,7 @@ class CommandOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t index_adjust = 0;
|
size_t index_adjust = 0;
|
||||||
constexpr int kSpacerNum = 5;
|
constexpr int kSpacerNum = 10;
|
||||||
auto need_value = registration_order_options_[i]->IsRequiredValue();
|
auto need_value = registration_order_options_[i]->IsRequiredValue();
|
||||||
ostream << kOptionStyleNormal
|
ostream << kOptionStyleNormal
|
||||||
<< registration_order_options_[i]->GetName();
|
<< registration_order_options_[i]->GetName();
|
||||||
@ -240,10 +257,17 @@ class CommandOptions {
|
|||||||
std::string operator()(const std::string& value) { return value; }
|
std::string operator()(const std::string& value) { return value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ReaderDouble {
|
||||||
|
double operator()(const std::string& value) { return std::stod(value); }
|
||||||
|
};
|
||||||
|
|
||||||
class Option {
|
class Option {
|
||||||
public:
|
public:
|
||||||
Option(const std::string& name, const std::string& short_name,
|
Option(const std::string& name,
|
||||||
const std::string& description, bool required, bool required_value)
|
const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
bool required,
|
||||||
|
bool required_value)
|
||||||
: name_(name),
|
: name_(name),
|
||||||
short_name_(short_name),
|
short_name_(short_name),
|
||||||
description_(description),
|
description_(description),
|
||||||
@ -288,9 +312,12 @@ class CommandOptions {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class OptionValue : public Option {
|
class OptionValue : public Option {
|
||||||
public:
|
public:
|
||||||
OptionValue(const std::string& name, const std::string& short_name,
|
OptionValue(const std::string& name,
|
||||||
const std::string& description, const T& default_value,
|
const std::string& short_name,
|
||||||
bool required, bool required_value)
|
const std::string& description,
|
||||||
|
const T& default_value,
|
||||||
|
bool required,
|
||||||
|
bool required_value)
|
||||||
: Option(name, short_name, description, required, required_value),
|
: Option(name, short_name, description, required, required_value),
|
||||||
default_value_(default_value),
|
default_value_(default_value),
|
||||||
value_(default_value){};
|
value_(default_value){};
|
||||||
@ -316,10 +343,18 @@ class CommandOptions {
|
|||||||
template <typename T, typename F>
|
template <typename T, typename F>
|
||||||
class OptionValueReader : public OptionValue<T> {
|
class OptionValueReader : public OptionValue<T> {
|
||||||
public:
|
public:
|
||||||
OptionValueReader(const std::string& name, const std::string& short_name,
|
OptionValueReader(const std::string& name,
|
||||||
const std::string& description, const T default_value,
|
const std::string& short_name,
|
||||||
F reader, bool required, bool required_value)
|
const std::string& description,
|
||||||
: OptionValue<T>(name, short_name, description, default_value, required,
|
const T default_value,
|
||||||
|
F reader,
|
||||||
|
bool required,
|
||||||
|
bool required_value)
|
||||||
|
: OptionValue<T>(name,
|
||||||
|
short_name,
|
||||||
|
description,
|
||||||
|
default_value,
|
||||||
|
required,
|
||||||
required_value),
|
required_value),
|
||||||
reader_(reader) {}
|
reader_(reader) {}
|
||||||
~OptionValueReader() = default;
|
~OptionValueReader() = default;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 Sony Corporation. All rights reserved.
|
// Copyright 2022 Sony Corporation. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
@ -14,18 +14,24 @@
|
|||||||
class FlutterEmbedderOptions {
|
class FlutterEmbedderOptions {
|
||||||
public:
|
public:
|
||||||
FlutterEmbedderOptions() {
|
FlutterEmbedderOptions() {
|
||||||
options_.AddString("bundle", "b", "Path to Flutter app bundle", "./",
|
options_.AddString("bundle", "b", "Path to Flutter project bundle",
|
||||||
false);
|
"./bundle", true);
|
||||||
options_.AddWithoutValue("no-cursor", "n", "No mouse cursor/pointer",
|
options_.AddWithoutValue("no-cursor", "n", "No mouse cursor/pointer",
|
||||||
false);
|
false);
|
||||||
|
options_.AddInt("rotation", "r",
|
||||||
|
"Window rotation(degree) [0(default)|90|180|270]", 0,
|
||||||
|
false);
|
||||||
|
options_.AddDouble("force-scale-factor", "s",
|
||||||
|
"Force a scale factor instead using default value", 1.0,
|
||||||
|
false);
|
||||||
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
|
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
|
||||||
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
|
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
|
||||||
// no more options.
|
// no more options.
|
||||||
#elif defined(FLUTTER_TARGET_BACKEND_X11)
|
#elif defined(FLUTTER_TARGET_BACKEND_X11)
|
||||||
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
|
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
|
||||||
false);
|
false);
|
||||||
options_.AddInt("width", "w", "Flutter app window width", 1280, false);
|
options_.AddInt("width", "w", "Window width", 1280, false);
|
||||||
options_.AddInt("height", "h", "Flutter app window height", 720, false);
|
options_.AddInt("height", "h", "Window height", 720, false);
|
||||||
#else // FLUTTER_TARGET_BACKEND_WAYLAND
|
#else // FLUTTER_TARGET_BACKEND_WAYLAND
|
||||||
options_.AddWithoutValue("onscreen-keyboard", "k",
|
options_.AddWithoutValue("onscreen-keyboard", "k",
|
||||||
"Enable on-screen keyboard", false);
|
"Enable on-screen keyboard", false);
|
||||||
@ -33,8 +39,8 @@ class FlutterEmbedderOptions {
|
|||||||
"Enable window decorations", false);
|
"Enable window decorations", false);
|
||||||
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
|
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
|
||||||
false);
|
false);
|
||||||
options_.AddInt("width", "w", "Flutter app window width", 1280, false);
|
options_.AddInt("width", "w", "Window width", 1280, false);
|
||||||
options_.AddInt("height", "h", "Flutter app window height", 720, false);
|
options_.AddInt("height", "h", "Window height", 720, false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
~FlutterEmbedderOptions() = default;
|
~FlutterEmbedderOptions() = default;
|
||||||
@ -48,6 +54,34 @@ class FlutterEmbedderOptions {
|
|||||||
|
|
||||||
bundle_path_ = options_.GetValue<std::string>("bundle");
|
bundle_path_ = options_.GetValue<std::string>("bundle");
|
||||||
use_mouse_cursor_ = !options_.Exist("no-cursor");
|
use_mouse_cursor_ = !options_.Exist("no-cursor");
|
||||||
|
if (options_.Exist("rotation")) {
|
||||||
|
switch (options_.GetValue<int>("rotation")) {
|
||||||
|
case 90:
|
||||||
|
window_view_rotation_ =
|
||||||
|
flutter::FlutterViewController::ViewRotation::kRotation_90;
|
||||||
|
break;
|
||||||
|
case 180:
|
||||||
|
window_view_rotation_ =
|
||||||
|
flutter::FlutterViewController::ViewRotation::kRotation_180;
|
||||||
|
break;
|
||||||
|
case 270:
|
||||||
|
window_view_rotation_ =
|
||||||
|
flutter::FlutterViewController::ViewRotation::kRotation_270;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
window_view_rotation_ =
|
||||||
|
flutter::FlutterViewController::ViewRotation::kRotation_0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options_.Exist("force-scale-factor")) {
|
||||||
|
is_force_scale_factor_ = true;
|
||||||
|
scale_factor_ = options_.GetValue<double>("force-scale-factor");
|
||||||
|
} else {
|
||||||
|
is_force_scale_factor_ = false;
|
||||||
|
scale_factor_ = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
|
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
|
||||||
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
|
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
|
||||||
@ -86,6 +120,11 @@ class FlutterEmbedderOptions {
|
|||||||
}
|
}
|
||||||
int WindowWidth() const { return window_width_; }
|
int WindowWidth() const { return window_width_; }
|
||||||
int WindowHeight() const { return window_height_; }
|
int WindowHeight() const { return window_height_; }
|
||||||
|
flutter::FlutterViewController::ViewRotation WindowRotation() const {
|
||||||
|
return window_view_rotation_;
|
||||||
|
}
|
||||||
|
bool IsForceScaleFactor() const { return is_force_scale_factor_; }
|
||||||
|
double ScaleFactor() const { return scale_factor_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
commandline::CommandOptions options_;
|
commandline::CommandOptions options_;
|
||||||
@ -98,6 +137,10 @@ class FlutterEmbedderOptions {
|
|||||||
flutter::FlutterViewController::ViewMode::kNormal;
|
flutter::FlutterViewController::ViewMode::kNormal;
|
||||||
int window_width_ = 1280;
|
int window_width_ = 1280;
|
||||||
int window_height_ = 720;
|
int window_height_ = 720;
|
||||||
|
flutter::FlutterViewController::ViewRotation window_view_rotation_ =
|
||||||
|
flutter::FlutterViewController::ViewRotation::kRotation_0;
|
||||||
|
bool is_force_scale_factor_;
|
||||||
|
double scale_factor_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FLUTTER_EMBEDDER_OPTIONS_
|
#endif // FLUTTER_EMBEDDER_OPTIONS_
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 Sony Corporation. All rights reserved.
|
// Copyright 2022 Sony Corporation. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
@ -29,9 +29,12 @@ int main(int argc, char** argv) {
|
|||||||
view_properties.width = options.WindowWidth();
|
view_properties.width = options.WindowWidth();
|
||||||
view_properties.height = options.WindowHeight();
|
view_properties.height = options.WindowHeight();
|
||||||
view_properties.view_mode = options.WindowViewMode();
|
view_properties.view_mode = options.WindowViewMode();
|
||||||
|
view_properties.view_rotation = options.WindowRotation();
|
||||||
view_properties.use_mouse_cursor = options.IsUseMouseCursor();
|
view_properties.use_mouse_cursor = options.IsUseMouseCursor();
|
||||||
view_properties.use_onscreen_keyboard = options.IsUseOnscreenKeyboard();
|
view_properties.use_onscreen_keyboard = options.IsUseOnscreenKeyboard();
|
||||||
view_properties.use_window_decoration = options.IsUseWindowDecoraation();
|
view_properties.use_window_decoration = options.IsUseWindowDecoraation();
|
||||||
|
view_properties.force_scale_factor = options.IsForceScaleFactor();
|
||||||
|
view_properties.scale_factor = options.ScaleFactor();
|
||||||
|
|
||||||
// The Flutter instance hosted by this window.
|
// The Flutter instance hosted by this window.
|
||||||
FlutterWindow window(view_properties, project);
|
FlutterWindow window(view_properties, project);
|
||||||
|
@ -2,19 +2,22 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// ignore_for_file: public_member_api_docs
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
|
/// Camera example home widget.
|
||||||
class CameraExampleHome extends StatefulWidget {
|
class CameraExampleHome extends StatefulWidget {
|
||||||
|
/// Default Constructor
|
||||||
|
const CameraExampleHome({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_CameraExampleHomeState createState() {
|
State<CameraExampleHome> createState() {
|
||||||
return _CameraExampleHomeState();
|
return _CameraExampleHomeState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,7 +36,7 @@ IconData getCameraLensIcon(CameraLensDirection direction) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void logError(String code, String? message) {
|
void _logError(String code, String? message) {
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
print('Error: $code\nError Message: $message');
|
print('Error: $code\nError Message: $message');
|
||||||
} else {
|
} else {
|
||||||
@ -105,6 +108,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #docregion AppLifecycle
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
final CameraController? cameraController = controller;
|
final CameraController? cameraController = controller;
|
||||||
@ -120,13 +124,11 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
onNewCameraSelected(cameraController.description);
|
onNewCameraSelected(cameraController.description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// #enddocregion AppLifecycle
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: _scaffoldKey,
|
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Camera example'),
|
title: const Text('Camera example'),
|
||||||
),
|
),
|
||||||
@ -134,12 +136,6 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(1.0),
|
|
||||||
child: Center(
|
|
||||||
child: _cameraPreviewWidget(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
@ -150,6 +146,12 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
width: 3.0,
|
width: 3.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(1.0),
|
||||||
|
child: Center(
|
||||||
|
child: _cameraPreviewWidget(),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_captureControlRowWidget(),
|
_captureControlRowWidget(),
|
||||||
@ -157,7 +159,6 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(5.0),
|
padding: const EdgeInsets.all(5.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_cameraTogglesRowWidget(),
|
_cameraTogglesRowWidget(),
|
||||||
_thumbnailWidget(),
|
_thumbnailWidget(),
|
||||||
@ -194,7 +195,8 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onScaleStart: _handleScaleStart,
|
onScaleStart: _handleScaleStart,
|
||||||
onScaleUpdate: _handleScaleUpdate,
|
onScaleUpdate: _handleScaleUpdate,
|
||||||
onTapDown: (details) => onViewFinderTap(details, constraints),
|
onTapDown: (TapDownDetails details) =>
|
||||||
|
onViewFinderTap(details, constraints),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -228,27 +230,34 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
localVideoController == null && imageFile == null
|
if (localVideoController == null && imageFile == null)
|
||||||
? Container()
|
Container()
|
||||||
: SizedBox(
|
else
|
||||||
child: (localVideoController == null)
|
SizedBox(
|
||||||
? Image.file(File(imageFile!.path))
|
width: 64.0,
|
||||||
: Container(
|
height: 64.0,
|
||||||
child: Center(
|
child: (localVideoController == null)
|
||||||
child: AspectRatio(
|
? (
|
||||||
aspectRatio:
|
// The captured image on the web contains a network-accessible URL
|
||||||
localVideoController.value.size != null
|
// pointing to a location within the browser. It may be displayed
|
||||||
? localVideoController
|
// either with Image.network or Image.memory after loading the image
|
||||||
.value.aspectRatio
|
// bytes to memory.
|
||||||
: 1.0,
|
kIsWeb
|
||||||
child: VideoPlayer(localVideoController)),
|
? Image.network(imageFile!.path)
|
||||||
),
|
: Image.file(File(imageFile!.path)))
|
||||||
decoration: BoxDecoration(
|
: Container(
|
||||||
border: Border.all(color: Colors.pink)),
|
decoration: BoxDecoration(
|
||||||
),
|
border: Border.all(color: Colors.pink)),
|
||||||
width: 64.0,
|
child: Center(
|
||||||
height: 64.0,
|
child: AspectRatio(
|
||||||
),
|
aspectRatio:
|
||||||
|
localVideoController.value.size != null
|
||||||
|
? localVideoController.value.aspectRatio
|
||||||
|
: 1.0,
|
||||||
|
child: VideoPlayer(localVideoController)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -258,27 +267,33 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
/// Display a bar with buttons to change the flash and exposure modes
|
/// Display a bar with buttons to change the flash and exposure modes
|
||||||
Widget _modeControlRowWidget() {
|
Widget _modeControlRowWidget() {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.flash_on),
|
icon: const Icon(Icons.flash_on),
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
onPressed: controller != null ? onFlashModeButtonPressed : null,
|
onPressed: controller != null ? onFlashModeButtonPressed : null,
|
||||||
),
|
),
|
||||||
IconButton(
|
// The exposure and focus mode are currently not supported on the web.
|
||||||
icon: Icon(Icons.exposure),
|
...!kIsWeb
|
||||||
color: Colors.blue,
|
? <Widget>[
|
||||||
onPressed:
|
IconButton(
|
||||||
controller != null ? onExposureModeButtonPressed : null,
|
icon: const Icon(Icons.exposure),
|
||||||
),
|
color: Colors.blue,
|
||||||
IconButton(
|
onPressed: controller != null
|
||||||
icon: Icon(Icons.filter_center_focus),
|
? onExposureModeButtonPressed
|
||||||
color: Colors.blue,
|
: null,
|
||||||
onPressed: controller != null ? onFocusModeButtonPressed : null,
|
),
|
||||||
),
|
IconButton(
|
||||||
|
icon: const Icon(Icons.filter_center_focus),
|
||||||
|
color: Colors.blue,
|
||||||
|
onPressed:
|
||||||
|
controller != null ? onFocusModeButtonPressed : null,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
: <Widget>[],
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute),
|
icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute),
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
@ -308,10 +323,9 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
child: ClipRect(
|
child: ClipRect(
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
mainAxisSize: MainAxisSize.max,
|
children: <Widget>[
|
||||||
children: [
|
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.flash_off),
|
icon: const Icon(Icons.flash_off),
|
||||||
color: controller?.value.flashMode == FlashMode.off
|
color: controller?.value.flashMode == FlashMode.off
|
||||||
? Colors.orange
|
? Colors.orange
|
||||||
: Colors.blue,
|
: Colors.blue,
|
||||||
@ -320,7 +334,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.flash_auto),
|
icon: const Icon(Icons.flash_auto),
|
||||||
color: controller?.value.flashMode == FlashMode.auto
|
color: controller?.value.flashMode == FlashMode.auto
|
||||||
? Colors.orange
|
? Colors.orange
|
||||||
: Colors.blue,
|
: Colors.blue,
|
||||||
@ -329,7 +343,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.flash_on),
|
icon: const Icon(Icons.flash_on),
|
||||||
color: controller?.value.flashMode == FlashMode.always
|
color: controller?.value.flashMode == FlashMode.always
|
||||||
? Colors.orange
|
? Colors.orange
|
||||||
: Colors.blue,
|
: Colors.blue,
|
||||||
@ -338,7 +352,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.highlight),
|
icon: const Icon(Icons.highlight),
|
||||||
color: controller?.value.flashMode == FlashMode.torch
|
color: controller?.value.flashMode == FlashMode.torch
|
||||||
? Colors.orange
|
? Colors.orange
|
||||||
: Colors.blue,
|
: Colors.blue,
|
||||||
@ -354,11 +368,15 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
|
|
||||||
Widget _exposureModeControlRowWidget() {
|
Widget _exposureModeControlRowWidget() {
|
||||||
final ButtonStyle styleAuto = TextButton.styleFrom(
|
final ButtonStyle styleAuto = TextButton.styleFrom(
|
||||||
|
// TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724
|
||||||
|
// ignore: deprecated_member_use
|
||||||
primary: controller?.value.exposureMode == ExposureMode.auto
|
primary: controller?.value.exposureMode == ExposureMode.auto
|
||||||
? Colors.orange
|
? Colors.orange
|
||||||
: Colors.blue,
|
: Colors.blue,
|
||||||
);
|
);
|
||||||
final ButtonStyle styleLocked = TextButton.styleFrom(
|
final ButtonStyle styleLocked = TextButton.styleFrom(
|
||||||
|
// TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724
|
||||||
|
// ignore: deprecated_member_use
|
||||||
primary: controller?.value.exposureMode == ExposureMode.locked
|
primary: controller?.value.exposureMode == ExposureMode.locked
|
||||||
? Colors.orange
|
? Colors.orange
|
||||||
: Colors.blue,
|
: Colors.blue,
|
||||||
@ -370,16 +388,14 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.grey.shade50,
|
color: Colors.grey.shade50,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Center(
|
const Center(
|
||||||
child: Text("Exposure Mode"),
|
child: Text('Exposure Mode'),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
mainAxisSize: MainAxisSize.max,
|
children: <Widget>[
|
||||||
children: [
|
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text('AUTO'),
|
|
||||||
style: styleAuto,
|
style: styleAuto,
|
||||||
onPressed: controller != null
|
onPressed: controller != null
|
||||||
? () =>
|
? () =>
|
||||||
@ -391,24 +407,31 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
showInSnackBar('Resetting exposure point');
|
showInSnackBar('Resetting exposure point');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
child: const Text('AUTO'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text('LOCKED'),
|
|
||||||
style: styleLocked,
|
style: styleLocked,
|
||||||
onPressed: controller != null
|
onPressed: controller != null
|
||||||
? () =>
|
? () =>
|
||||||
onSetExposureModeButtonPressed(ExposureMode.locked)
|
onSetExposureModeButtonPressed(ExposureMode.locked)
|
||||||
: null,
|
: null,
|
||||||
|
child: const Text('LOCKED'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
style: styleLocked,
|
||||||
|
onPressed: controller != null
|
||||||
|
? () => controller!.setExposureOffset(0.0)
|
||||||
|
: null,
|
||||||
|
child: const Text('RESET OFFSET'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Center(
|
const Center(
|
||||||
child: Text("Exposure Offset"),
|
child: Text('Exposure Offset'),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
mainAxisSize: MainAxisSize.max,
|
children: <Widget>[
|
||||||
children: [
|
|
||||||
Text(_minAvailableExposureOffset.toString()),
|
Text(_minAvailableExposureOffset.toString()),
|
||||||
Slider(
|
Slider(
|
||||||
value: _currentExposureOffset,
|
value: _currentExposureOffset,
|
||||||
@ -432,11 +455,15 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
|
|
||||||
Widget _focusModeControlRowWidget() {
|
Widget _focusModeControlRowWidget() {
|
||||||
final ButtonStyle styleAuto = TextButton.styleFrom(
|
final ButtonStyle styleAuto = TextButton.styleFrom(
|
||||||
|
// TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724
|
||||||
|
// ignore: deprecated_member_use
|
||||||
primary: controller?.value.focusMode == FocusMode.auto
|
primary: controller?.value.focusMode == FocusMode.auto
|
||||||
? Colors.orange
|
? Colors.orange
|
||||||
: Colors.blue,
|
: Colors.blue,
|
||||||
);
|
);
|
||||||
final ButtonStyle styleLocked = TextButton.styleFrom(
|
final ButtonStyle styleLocked = TextButton.styleFrom(
|
||||||
|
// TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724
|
||||||
|
// ignore: deprecated_member_use
|
||||||
primary: controller?.value.focusMode == FocusMode.locked
|
primary: controller?.value.focusMode == FocusMode.locked
|
||||||
? Colors.orange
|
? Colors.orange
|
||||||
: Colors.blue,
|
: Colors.blue,
|
||||||
@ -448,31 +475,32 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.grey.shade50,
|
color: Colors.grey.shade50,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Center(
|
const Center(
|
||||||
child: Text("Focus Mode"),
|
child: Text('Focus Mode'),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
mainAxisSize: MainAxisSize.max,
|
children: <Widget>[
|
||||||
children: [
|
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text('AUTO'),
|
|
||||||
style: styleAuto,
|
style: styleAuto,
|
||||||
onPressed: controller != null
|
onPressed: controller != null
|
||||||
? () => onSetFocusModeButtonPressed(FocusMode.auto)
|
? () => onSetFocusModeButtonPressed(FocusMode.auto)
|
||||||
: null,
|
: null,
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (controller != null) controller!.setFocusPoint(null);
|
if (controller != null) {
|
||||||
|
controller!.setFocusPoint(null);
|
||||||
|
}
|
||||||
showInSnackBar('Resetting focus point');
|
showInSnackBar('Resetting focus point');
|
||||||
},
|
},
|
||||||
|
child: const Text('AUTO'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text('LOCKED'),
|
|
||||||
style: styleLocked,
|
style: styleLocked,
|
||||||
onPressed: controller != null
|
onPressed: controller != null
|
||||||
? () => onSetFocusModeButtonPressed(FocusMode.locked)
|
? () => onSetFocusModeButtonPressed(FocusMode.locked)
|
||||||
: null,
|
: null,
|
||||||
|
child: const Text('LOCKED'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -489,7 +517,6 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.camera_alt),
|
icon: const Icon(Icons.camera_alt),
|
||||||
@ -512,8 +539,8 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: cameraController != null &&
|
icon: cameraController != null &&
|
||||||
cameraController.value.isRecordingPaused
|
cameraController.value.isRecordingPaused
|
||||||
? Icon(Icons.play_arrow)
|
? const Icon(Icons.play_arrow)
|
||||||
: Icon(Icons.pause),
|
: const Icon(Icons.pause),
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
onPressed: cameraController != null &&
|
onPressed: cameraController != null &&
|
||||||
cameraController.value.isInitialized &&
|
cameraController.value.isInitialized &&
|
||||||
@ -531,7 +558,16 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
cameraController.value.isRecordingVideo
|
cameraController.value.isRecordingVideo
|
||||||
? onStopButtonPressed
|
? onStopButtonPressed
|
||||||
: null,
|
: null,
|
||||||
)
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.pause_presentation),
|
||||||
|
color:
|
||||||
|
cameraController != null && cameraController.value.isPreviewPaused
|
||||||
|
? Colors.red
|
||||||
|
: Colors.blue,
|
||||||
|
onPressed:
|
||||||
|
cameraController == null ? null : onPausePreviewButtonPressed,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -540,18 +576,21 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
Widget _cameraTogglesRowWidget() {
|
Widget _cameraTogglesRowWidget() {
|
||||||
final List<Widget> toggles = <Widget>[];
|
final List<Widget> toggles = <Widget>[];
|
||||||
|
|
||||||
final onChanged = (CameraDescription? description) {
|
void onChanged(CameraDescription? description) {
|
||||||
if (description == null) {
|
if (description == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onNewCameraSelected(description);
|
onNewCameraSelected(description);
|
||||||
};
|
}
|
||||||
|
|
||||||
if (cameras.isEmpty) {
|
if (_cameras.isEmpty) {
|
||||||
return const Text('No camera found');
|
_ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async {
|
||||||
|
showInSnackBar('No camera found.');
|
||||||
|
});
|
||||||
|
return const Text('None');
|
||||||
} else {
|
} else {
|
||||||
for (CameraDescription cameraDescription in cameras) {
|
for (final CameraDescription cameraDescription in _cameras) {
|
||||||
toggles.add(
|
toggles.add(
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 90.0,
|
width: 90.0,
|
||||||
@ -575,8 +614,8 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
|
String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
|
||||||
|
|
||||||
void showInSnackBar(String message) {
|
void showInSnackBar(String message) {
|
||||||
// ignore: deprecated_member_use
|
ScaffoldMessenger.of(context)
|
||||||
_scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text(message)));
|
.showSnackBar(SnackBar(content: Text(message)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) {
|
void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) {
|
||||||
@ -586,7 +625,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
|
|
||||||
final CameraController cameraController = controller!;
|
final CameraController cameraController = controller!;
|
||||||
|
|
||||||
final offset = Offset(
|
final Offset offset = Offset(
|
||||||
details.localPosition.dx / constraints.maxWidth,
|
details.localPosition.dx / constraints.maxWidth,
|
||||||
details.localPosition.dy / constraints.maxHeight,
|
details.localPosition.dy / constraints.maxHeight,
|
||||||
);
|
);
|
||||||
@ -594,21 +633,32 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
cameraController.setFocusPoint(offset);
|
cameraController.setFocusPoint(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNewCameraSelected(CameraDescription cameraDescription) async {
|
Future<void> onNewCameraSelected(CameraDescription cameraDescription) async {
|
||||||
if (controller != null) {
|
final CameraController? oldController = controller;
|
||||||
await controller!.dispose();
|
if (oldController != null) {
|
||||||
|
// `controller` needs to be set to null before getting disposed,
|
||||||
|
// to avoid a race condition when we use the controller that is being
|
||||||
|
// disposed. This happens when camera permission dialog shows up,
|
||||||
|
// which triggers `didChangeAppLifecycleState`, which disposes and
|
||||||
|
// re-creates the controller.
|
||||||
|
controller = null;
|
||||||
|
await oldController.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
final CameraController cameraController = CameraController(
|
final CameraController cameraController = CameraController(
|
||||||
cameraDescription,
|
cameraDescription,
|
||||||
ResolutionPreset.medium,
|
kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium,
|
||||||
enableAudio: enableAudio,
|
enableAudio: enableAudio,
|
||||||
imageFormatGroup: ImageFormatGroup.jpeg,
|
imageFormatGroup: ImageFormatGroup.jpeg,
|
||||||
);
|
);
|
||||||
|
|
||||||
controller = cameraController;
|
controller = cameraController;
|
||||||
|
|
||||||
// If the controller is updated then update the UI.
|
// If the controller is updated then update the UI.
|
||||||
cameraController.addListener(() {
|
cameraController.addListener(() {
|
||||||
if (mounted) setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
if (cameraController.value.hasError) {
|
if (cameraController.value.hasError) {
|
||||||
showInSnackBar(
|
showInSnackBar(
|
||||||
'Camera error ${cameraController.value.errorDescription}');
|
'Camera error ${cameraController.value.errorDescription}');
|
||||||
@ -617,22 +667,52 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await cameraController.initialize();
|
await cameraController.initialize();
|
||||||
await Future.wait([
|
await Future.wait(<Future<Object?>>[
|
||||||
cameraController
|
// The exposure mode is currently not supported on the web.
|
||||||
.getMinExposureOffset()
|
...!kIsWeb
|
||||||
.then((value) => _minAvailableExposureOffset = value),
|
? <Future<Object?>>[
|
||||||
cameraController
|
cameraController.getMinExposureOffset().then(
|
||||||
.getMaxExposureOffset()
|
(double value) => _minAvailableExposureOffset = value),
|
||||||
.then((value) => _maxAvailableExposureOffset = value),
|
cameraController
|
||||||
|
.getMaxExposureOffset()
|
||||||
|
.then((double value) => _maxAvailableExposureOffset = value)
|
||||||
|
]
|
||||||
|
: <Future<Object?>>[],
|
||||||
cameraController
|
cameraController
|
||||||
.getMaxZoomLevel()
|
.getMaxZoomLevel()
|
||||||
.then((value) => _maxAvailableZoom = value),
|
.then((double value) => _maxAvailableZoom = value),
|
||||||
cameraController
|
cameraController
|
||||||
.getMinZoomLevel()
|
.getMinZoomLevel()
|
||||||
.then((value) => _minAvailableZoom = value),
|
.then((double value) => _minAvailableZoom = value),
|
||||||
]);
|
]);
|
||||||
} on CameraException catch (e) {
|
} on CameraException catch (e) {
|
||||||
_showCameraException(e);
|
switch (e.code) {
|
||||||
|
case 'CameraAccessDenied':
|
||||||
|
showInSnackBar('You have denied camera access.');
|
||||||
|
break;
|
||||||
|
case 'CameraAccessDeniedWithoutPrompt':
|
||||||
|
// iOS only
|
||||||
|
showInSnackBar('Please go to Settings app to enable camera access.');
|
||||||
|
break;
|
||||||
|
case 'CameraAccessRestricted':
|
||||||
|
// iOS only
|
||||||
|
showInSnackBar('Camera access is restricted.');
|
||||||
|
break;
|
||||||
|
case 'AudioAccessDenied':
|
||||||
|
showInSnackBar('You have denied audio access.');
|
||||||
|
break;
|
||||||
|
case 'AudioAccessDeniedWithoutPrompt':
|
||||||
|
// iOS only
|
||||||
|
showInSnackBar('Please go to Settings app to enable audio access.');
|
||||||
|
break;
|
||||||
|
case 'AudioAccessRestricted':
|
||||||
|
// iOS only
|
||||||
|
showInSnackBar('Audio access is restricted.');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_showCameraException(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -648,7 +728,9 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
videoController?.dispose();
|
videoController?.dispose();
|
||||||
videoController = null;
|
videoController = null;
|
||||||
});
|
});
|
||||||
if (file != null) showInSnackBar('Picture saved to ${file.path}');
|
if (file != null) {
|
||||||
|
showInSnackBar('Picture saved to ${file.path}');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -690,50 +772,64 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onCaptureOrientationLockButtonPressed() async {
|
Future<void> onCaptureOrientationLockButtonPressed() async {
|
||||||
if (controller != null) {
|
try {
|
||||||
final CameraController cameraController = controller!;
|
if (controller != null) {
|
||||||
if (cameraController.value.isCaptureOrientationLocked) {
|
final CameraController cameraController = controller!;
|
||||||
await cameraController.unlockCaptureOrientation();
|
if (cameraController.value.isCaptureOrientationLocked) {
|
||||||
showInSnackBar('Capture orientation unlocked');
|
await cameraController.unlockCaptureOrientation();
|
||||||
} else {
|
showInSnackBar('Capture orientation unlocked');
|
||||||
await cameraController.lockCaptureOrientation();
|
} else {
|
||||||
showInSnackBar(
|
await cameraController.lockCaptureOrientation();
|
||||||
'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}');
|
showInSnackBar(
|
||||||
|
'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} on CameraException catch (e) {
|
||||||
|
_showCameraException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSetFlashModeButtonPressed(FlashMode mode) {
|
void onSetFlashModeButtonPressed(FlashMode mode) {
|
||||||
setFlashMode(mode).then((_) {
|
setFlashMode(mode).then((_) {
|
||||||
if (mounted) setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
showInSnackBar('Flash mode set to ${mode.toString().split('.').last}');
|
showInSnackBar('Flash mode set to ${mode.toString().split('.').last}');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSetExposureModeButtonPressed(ExposureMode mode) {
|
void onSetExposureModeButtonPressed(ExposureMode mode) {
|
||||||
setExposureMode(mode).then((_) {
|
setExposureMode(mode).then((_) {
|
||||||
if (mounted) setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
showInSnackBar('Exposure mode set to ${mode.toString().split('.').last}');
|
showInSnackBar('Exposure mode set to ${mode.toString().split('.').last}');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSetFocusModeButtonPressed(FocusMode mode) {
|
void onSetFocusModeButtonPressed(FocusMode mode) {
|
||||||
setFocusMode(mode).then((_) {
|
setFocusMode(mode).then((_) {
|
||||||
if (mounted) setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
showInSnackBar('Focus mode set to ${mode.toString().split('.').last}');
|
showInSnackBar('Focus mode set to ${mode.toString().split('.').last}');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void onVideoRecordButtonPressed() {
|
void onVideoRecordButtonPressed() {
|
||||||
startVideoRecording().then((_) {
|
startVideoRecording().then((_) {
|
||||||
if (mounted) setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void onStopButtonPressed() {
|
void onStopButtonPressed() {
|
||||||
stopVideoRecording().then((file) {
|
stopVideoRecording().then((XFile? file) {
|
||||||
if (mounted) setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
showInSnackBar('Video recorded to ${file.path}');
|
showInSnackBar('Video recorded to ${file.path}');
|
||||||
videoFile = file;
|
videoFile = file;
|
||||||
@ -742,16 +838,39 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> onPausePreviewButtonPressed() async {
|
||||||
|
final CameraController? cameraController = controller;
|
||||||
|
|
||||||
|
if (cameraController == null || !cameraController.value.isInitialized) {
|
||||||
|
showInSnackBar('Error: select a camera first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cameraController.value.isPreviewPaused) {
|
||||||
|
await cameraController.resumePreview();
|
||||||
|
} else {
|
||||||
|
await cameraController.pausePreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void onPauseButtonPressed() {
|
void onPauseButtonPressed() {
|
||||||
pauseVideoRecording().then((_) {
|
pauseVideoRecording().then((_) {
|
||||||
if (mounted) setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
showInSnackBar('Video recording paused');
|
showInSnackBar('Video recording paused');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void onResumeButtonPressed() {
|
void onResumeButtonPressed() {
|
||||||
resumeVideoRecording().then((_) {
|
resumeVideoRecording().then((_) {
|
||||||
if (mounted) setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
showInSnackBar('Video recording resumed');
|
showInSnackBar('Video recording resumed');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -796,7 +915,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
final CameraController? cameraController = controller;
|
final CameraController? cameraController = controller;
|
||||||
|
|
||||||
if (cameraController == null || !cameraController.value.isRecordingVideo) {
|
if (cameraController == null || !cameraController.value.isRecordingVideo) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -811,7 +930,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
final CameraController? cameraController = controller;
|
final CameraController? cameraController = controller;
|
||||||
|
|
||||||
if (cameraController == null || !cameraController.value.isRecordingVideo) {
|
if (cameraController == null || !cameraController.value.isRecordingVideo) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -882,12 +1001,16 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final VideoPlayerController vController =
|
final VideoPlayerController vController = kIsWeb
|
||||||
VideoPlayerController.file(File(videoFile!.path));
|
? VideoPlayerController.network(videoFile!.path)
|
||||||
|
: VideoPlayerController.file(File(videoFile!.path));
|
||||||
|
|
||||||
videoPlayerListener = () {
|
videoPlayerListener = () {
|
||||||
if (videoController != null && videoController!.value.size != null) {
|
if (videoController != null && videoController!.value.size != null) {
|
||||||
// Refreshing the state to update video player with the correct ratio.
|
// Refreshing the state to update video player with the correct ratio.
|
||||||
if (mounted) setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
videoController!.removeListener(videoPlayerListener!);
|
videoController!.removeListener(videoPlayerListener!);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -917,7 +1040,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
XFile file = await cameraController.takePicture();
|
final XFile file = await cameraController.takePicture();
|
||||||
return file;
|
return file;
|
||||||
} on CameraException catch (e) {
|
} on CameraException catch (e) {
|
||||||
_showCameraException(e);
|
_showCameraException(e);
|
||||||
@ -926,41 +1049,35 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showCameraException(CameraException e) {
|
void _showCameraException(CameraException e) {
|
||||||
logError(e.code, e.description);
|
_logError(e.code, e.description);
|
||||||
showInSnackBar('Error: ${e.code}\n${e.description}');
|
showInSnackBar('Error: ${e.code}\n${e.description}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// CameraApp is the Main Application.
|
||||||
class CameraApp extends StatelessWidget {
|
class CameraApp extends StatelessWidget {
|
||||||
|
/// Default Constructor
|
||||||
|
const CameraApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return const MaterialApp(
|
||||||
scrollBehavior: MyCustomScrollBehavior(),
|
|
||||||
home: CameraExampleHome(),
|
home: CameraExampleHome(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyCustomScrollBehavior extends MaterialScrollBehavior {
|
List<CameraDescription> _cameras = <CameraDescription>[];
|
||||||
// Override behavior methods and getters like dragDevices
|
|
||||||
@override
|
|
||||||
Set<PointerDeviceKind> get dragDevices => {
|
|
||||||
PointerDeviceKind.touch,
|
|
||||||
PointerDeviceKind.mouse,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
List<CameraDescription> cameras = [];
|
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
// Fetch the available cameras before initializing the app.
|
// Fetch the available cameras before initializing the app.
|
||||||
try {
|
try {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
cameras = await availableCameras();
|
_cameras = await availableCameras();
|
||||||
} on CameraException catch (e) {
|
} on CameraException catch (e) {
|
||||||
logError(e.code, e.description);
|
_logError(e.code, e.description);
|
||||||
}
|
}
|
||||||
runApp(CameraApp());
|
runApp(const CameraApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This allows a value of type T or T? to be treated as a value of type T?.
|
/// This allows a value of type T or T? to be treated as a value of type T?.
|
||||||
|
@ -4,18 +4,17 @@ publish_to: none
|
|||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.12.0 <3.0.0"
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
flutter: ">=1.22.0"
|
flutter: ">=2.10.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
camera: ^0.8.1+7
|
camera: ^0.10.0+1
|
||||||
camera_elinux:
|
camera_elinux:
|
||||||
path: ../
|
path: ../
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
path_provider: ^2.0.0
|
|
||||||
path_provider_elinux:
|
path_provider_elinux:
|
||||||
path: ../../path_provider
|
path: ../../path_provider
|
||||||
video_player: ^2.1.4
|
video_player: ^2.4.7
|
||||||
video_player_elinux:
|
video_player_elinux:
|
||||||
path: ../../video_player
|
path: ../../video_player
|
||||||
|
|
||||||
@ -26,7 +25,6 @@ dev_dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
integration_test:
|
integration_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
pedantic: ^1.10.0
|
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
@ -2,13 +2,13 @@ name: camera_elinux
|
|||||||
description: A Flutter plugin for getting information about and controlling the
|
description: A Flutter plugin for getting information about and controlling the
|
||||||
camera on eLinux. Supports previewing the camera feed, capturing images, capturing video,
|
camera on eLinux. Supports previewing the camera feed, capturing images, capturing video,
|
||||||
and streaming image buffers to dart.
|
and streaming image buffers to dart.
|
||||||
version: 0.2.0
|
version: 0.2.1
|
||||||
homepage: https://github.com/sony/flutter-elinux-plugins
|
homepage: https://github.com/sony/flutter-elinux-plugins
|
||||||
repository: https://github.com/sony/flutter-elinux-plugins/tree/main/packages/camera
|
repository: https://github.com/sony/flutter-elinux-plugins/tree/main/packages/camera
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.12.0 <3.0.0"
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
flutter: ">=1.20.0"
|
flutter: ">=2.10.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
|
Reference in New Issue
Block a user