Add shared_preferences (#15)
This commit is contained in:
committed by
GitHub
parent
6eed119327
commit
50c4792056
@@ -0,0 +1,7 @@
|
|||||||
|
.DS_Store
|
||||||
|
.dart_tool/
|
||||||
|
|
||||||
|
.packages
|
||||||
|
.pub/
|
||||||
|
|
||||||
|
build/
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
## 1.0.0
|
||||||
|
* Initial release to support shared_preferences on eLinux.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# shared\_preferences\_elinux
|
||||||
|
|
||||||
|
The eLinux implementation of [`shared_preferences`](https://github.com/flutter/plugins/tree/master/packages/shared_preferences).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### pubspec.yaml
|
||||||
|
```Yaml
|
||||||
|
dependencies:
|
||||||
|
shared_preferences_elinux:
|
||||||
|
git:
|
||||||
|
url: https://github.com/sony/flutter-elinux-plugins.git
|
||||||
|
path: packages/shared_preferences
|
||||||
|
ref: main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Source code
|
||||||
|
Import `shared_preferences_elinux` in your Dart code:
|
||||||
|
|
||||||
|
```Dart
|
||||||
|
import 'package:shared_preferences_elinux/shared_preferences_elinux.dart';
|
||||||
|
```
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.packages
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Web related
|
||||||
|
lib/generated_plugin_registrant.dart
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Exceptions to above rules.
|
||||||
|
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# shared_preferences_elinux_example
|
||||||
|
|
||||||
|
Demonstrates how to use the `shared_preferences_elinux` plugin.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
For help getting started with Flutter for eLinux, view our online
|
||||||
|
[documentation](https://github.com/sony/flutter-elinux/wiki).
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
flutter/ephemeral/
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(runner LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(BINARY_NAME "shared_preferences_elinux_example")
|
||||||
|
|
||||||
|
cmake_policy(SET CMP0079 NEW)
|
||||||
|
|
||||||
|
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
|
||||||
|
|
||||||
|
# Root filesystem for cross-building.
|
||||||
|
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
|
||||||
|
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
|
||||||
|
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE 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.
|
||||||
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
|
set(CMAKE_BUILD_TYPE "Debug" CACHE
|
||||||
|
STRING "Flutter build mode" FORCE)
|
||||||
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||||
|
"Debug" "Profile" "Release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Configure build option to target backend.
|
||||||
|
if (NOT FLUTTER_TARGET_BACKEND_TYPE)
|
||||||
|
set(FLUTTER_TARGET_BACKEND_TYPE "wayland" CACHE
|
||||||
|
STRING "Flutter target backend type" FORCE)
|
||||||
|
set_property(CACHE FLUTTER_TARGET_BACKEND_TYPE PROPERTY STRINGS
|
||||||
|
"wayland" "gbm" "eglstream" "x11")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Compilation settings that should be applied to most targets.
|
||||||
|
function(APPLY_STANDARD_SETTINGS TARGET)
|
||||||
|
target_compile_features(${TARGET} PUBLIC cxx_std_17)
|
||||||
|
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
|
||||||
|
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
|
||||||
|
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
|
||||||
|
|
||||||
|
# Flutter library and tool build rules.
|
||||||
|
add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||||
|
|
||||||
|
# Application build
|
||||||
|
add_subdirectory("runner")
|
||||||
|
|
||||||
|
# Generated plugin build rules, which manage building the plugins and adding
|
||||||
|
# them to the application.
|
||||||
|
include(flutter/generated_plugins.cmake)
|
||||||
|
|
||||||
|
# === Installation ===
|
||||||
|
# By default, "installing" just makes a relocatable bundle in the build
|
||||||
|
# directory.
|
||||||
|
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
|
||||||
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
|
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Start with a clean build bundle directory every time.
|
||||||
|
install(CODE "
|
||||||
|
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
|
||||||
|
" COMPONENT Runtime)
|
||||||
|
|
||||||
|
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
||||||
|
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
|
||||||
|
|
||||||
|
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
install(FILES "${FLUTTER_LIBRARY}"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
install(FILES "${FLUTTER_EMBEDDER_LIBRARY}"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
if(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||||
|
# from a previous install.
|
||||||
|
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||||
|
install(CODE "
|
||||||
|
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||||
|
" COMPONENT Runtime)
|
||||||
|
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||||
|
|
||||||
|
# Install the AOT library on non-Debug builds only.
|
||||||
|
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||||
|
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
|
||||||
|
|
||||||
|
# Configuration provided via flutter tool.
|
||||||
|
include(${EPHEMERAL_DIR}/generated_config.cmake)
|
||||||
|
|
||||||
|
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
|
||||||
|
|
||||||
|
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
|
||||||
|
# which isn't available in 3.10.
|
||||||
|
function(list_prepend LIST_NAME PREFIX)
|
||||||
|
set(NEW_LIST "")
|
||||||
|
foreach(element ${${LIST_NAME}})
|
||||||
|
list(APPEND NEW_LIST "${PREFIX}${element}")
|
||||||
|
endforeach(element)
|
||||||
|
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# === Flutter Library ===
|
||||||
|
# System-level dependencies.
|
||||||
|
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_engine.so")
|
||||||
|
if(FLUTTER_TARGET_BACKEND_TYPE MATCHES "gbm")
|
||||||
|
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_gbm.so")
|
||||||
|
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "eglstream")
|
||||||
|
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_eglstream.so")
|
||||||
|
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "x11")
|
||||||
|
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_x11.so")
|
||||||
|
else()
|
||||||
|
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_wayland.so")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Published to parent scope for install step.
|
||||||
|
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
|
||||||
|
set(FLUTTER_EMBEDDER_LIBRARY ${FLUTTER_EMBEDDER_LIBRARY} PARENT_SCOPE)
|
||||||
|
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
|
||||||
|
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/elinux/" PARENT_SCOPE)
|
||||||
|
set(AOT_LIBRARY "${EPHEMERAL_DIR}/libapp.so" PARENT_SCOPE)
|
||||||
|
|
||||||
|
list(APPEND FLUTTER_LIBRARY_HEADERS
|
||||||
|
"flutter_export.h"
|
||||||
|
"flutter_plugin_registrar.h"
|
||||||
|
"flutter_messenger.h"
|
||||||
|
"flutter_texture_registrar.h"
|
||||||
|
"flutter_elinux.h"
|
||||||
|
"flutter_platform_views.h"
|
||||||
|
)
|
||||||
|
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/")
|
||||||
|
add_library(flutter INTERFACE)
|
||||||
|
target_include_directories(flutter INTERFACE
|
||||||
|
"${EPHEMERAL_DIR}"
|
||||||
|
)
|
||||||
|
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
|
||||||
|
target_link_libraries(flutter INTERFACE "${FLUTTER_EMBEDDER_LIBRARY}")
|
||||||
|
add_dependencies(flutter flutter_assemble)
|
||||||
|
|
||||||
|
# === Wrapper ===
|
||||||
|
list(APPEND CPP_WRAPPER_SOURCES_CORE
|
||||||
|
"core_implementations.cc"
|
||||||
|
"standard_codec.cc"
|
||||||
|
)
|
||||||
|
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
|
||||||
|
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
|
||||||
|
"plugin_registrar.cc"
|
||||||
|
)
|
||||||
|
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
|
||||||
|
list(APPEND CPP_WRAPPER_SOURCES_APP
|
||||||
|
"flutter_engine.cc"
|
||||||
|
"flutter_view_controller.cc"
|
||||||
|
)
|
||||||
|
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
|
||||||
|
|
||||||
|
# Wrapper sources needed for a plugin.
|
||||||
|
add_library(flutter_wrapper_plugin STATIC
|
||||||
|
${CPP_WRAPPER_SOURCES_CORE}
|
||||||
|
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||||
|
)
|
||||||
|
apply_standard_settings(flutter_wrapper_plugin)
|
||||||
|
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||||
|
POSITION_INDEPENDENT_CODE ON)
|
||||||
|
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||||
|
CXX_VISIBILITY_PRESET hidden)
|
||||||
|
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
|
||||||
|
target_include_directories(flutter_wrapper_plugin PUBLIC
|
||||||
|
"${WRAPPER_ROOT}/include"
|
||||||
|
)
|
||||||
|
add_dependencies(flutter_wrapper_plugin flutter_assemble)
|
||||||
|
|
||||||
|
# Wrapper sources needed for the runner.
|
||||||
|
add_library(flutter_wrapper_app STATIC
|
||||||
|
${CPP_WRAPPER_SOURCES_CORE}
|
||||||
|
${CPP_WRAPPER_SOURCES_APP}
|
||||||
|
)
|
||||||
|
apply_standard_settings(flutter_wrapper_app)
|
||||||
|
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
|
||||||
|
target_include_directories(flutter_wrapper_app PUBLIC
|
||||||
|
"${WRAPPER_ROOT}/include"
|
||||||
|
)
|
||||||
|
add_dependencies(flutter_wrapper_app flutter_assemble)
|
||||||
|
|
||||||
|
add_custom_target(flutter_assemble DEPENDS
|
||||||
|
"${FLUTTER_LIBRARY}"
|
||||||
|
"${FLUTTER_EMBEDDER_LIBRARY}"
|
||||||
|
${FLUTTER_LIBRARY_HEADERS}
|
||||||
|
${CPP_WRAPPER_SOURCES_CORE}
|
||||||
|
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||||
|
${CPP_WRAPPER_SOURCES_APP}
|
||||||
|
)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||||
|
#define GENERATED_PLUGIN_REGISTRANT_
|
||||||
|
|
||||||
|
#include <flutter/plugin_registry.h>
|
||||||
|
|
||||||
|
// Registers Flutter plugins.
|
||||||
|
void RegisterPlugins(flutter::PluginRegistry* registry);
|
||||||
|
|
||||||
|
#endif // GENERATED_PLUGIN_REGISTRANT_
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#
|
||||||
|
# Generated file, do not edit.
|
||||||
|
#
|
||||||
|
|
||||||
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
)
|
||||||
|
|
||||||
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|
||||||
|
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||||
|
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/elinux plugins/${plugin})
|
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||||
|
endforeach(plugin)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
// @dart=2.12
|
||||||
|
|
||||||
|
import 'package:shared_preferences_elinux_example/main.dart' as entrypoint;
|
||||||
|
import 'generated_plugin_registrant.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
registerPlugins();
|
||||||
|
entrypoint.main();
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(runner LANGUAGES CXX)
|
||||||
|
|
||||||
|
if(FLUTTER_TARGET_BACKEND_TYPE MATCHES "gbm")
|
||||||
|
add_definitions(-DFLUTTER_TARGET_BACKEND_GBM)
|
||||||
|
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "eglstream")
|
||||||
|
add_definitions(-DFLUTTER_TARGET_BACKEND_EGLSTREAM)
|
||||||
|
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "x11")
|
||||||
|
add_definitions(-DFLUTTER_TARGET_BACKEND_X11)
|
||||||
|
else()
|
||||||
|
add_definitions(-DFLUTTER_TARGET_BACKEND_WAYLAND)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(${BINARY_NAME}
|
||||||
|
"flutter_window.cc"
|
||||||
|
"main.cc"
|
||||||
|
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
||||||
|
)
|
||||||
|
apply_standard_settings(${BINARY_NAME})
|
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
|
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
|
||||||
|
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||||
|
add_dependencies(${BINARY_NAME} flutter_assemble)
|
||||||
@@ -0,0 +1,367 @@
|
|||||||
|
// Copyright 2021 Sony Corporation. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef COMMAND_OPTIONS_
|
||||||
|
#define COMMAND_OPTIONS_
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// todo: Supports other types besides int, string.
|
||||||
|
|
||||||
|
namespace commandline {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr char kOptionStyleNormal[] = "--";
|
||||||
|
constexpr char kOptionStyleShort[] = "-";
|
||||||
|
constexpr char kOptionValueForHelpMessage[] = "=<value>";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class Exception : public std::exception {
|
||||||
|
public:
|
||||||
|
Exception(const std::string& msg) : msg_(msg) {}
|
||||||
|
~Exception() throw() {}
|
||||||
|
|
||||||
|
const char* what() const throw() { return msg_.c_str(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string msg_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CommandOptions {
|
||||||
|
public:
|
||||||
|
CommandOptions() = default;
|
||||||
|
~CommandOptions() = default;
|
||||||
|
|
||||||
|
void AddWithoutValue(const std::string& name, const std::string& short_name,
|
||||||
|
const std::string& description, bool required) {
|
||||||
|
Add<std::string, ReaderString>(name, short_name, description, "",
|
||||||
|
ReaderString(), required, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddInt(const std::string& name, const std::string& short_name,
|
||||||
|
const std::string& description, const int& default_value,
|
||||||
|
bool required) {
|
||||||
|
Add<int, ReaderInt>(name, short_name, description, default_value,
|
||||||
|
ReaderInt(), required, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddString(const std::string& name, const std::string& short_name,
|
||||||
|
const std::string& description,
|
||||||
|
const std::string& default_value, bool required) {
|
||||||
|
Add<std::string, ReaderString>(name, short_name, description, default_value,
|
||||||
|
ReaderString(), required, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename F>
|
||||||
|
void Add(const std::string& name, const std::string& short_name,
|
||||||
|
const std::string& description, const T default_value,
|
||||||
|
F reader = F(), bool required = true, bool required_value = true) {
|
||||||
|
if (options_.find(name) != options_.end()) {
|
||||||
|
std::cerr << "Already registered option: " << name << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lut_short_options_.find(short_name) != lut_short_options_.end()) {
|
||||||
|
std::cerr << short_name << "is already registered" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lut_short_options_[short_name] = name;
|
||||||
|
|
||||||
|
options_[name] = std::make_unique<OptionValueReader<T, F>>(
|
||||||
|
name, short_name, description, default_value, reader, required,
|
||||||
|
required_value);
|
||||||
|
|
||||||
|
// register to show help message.
|
||||||
|
registration_order_options_.push_back(options_[name].get());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exist(const std::string& name) {
|
||||||
|
auto itr = options_.find(name);
|
||||||
|
return itr != options_.end() && itr->second->HasValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& GetValue(const std::string& name) {
|
||||||
|
auto itr = options_.find(name);
|
||||||
|
if (itr == options_.end()) {
|
||||||
|
throw Exception("Not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* option_value = dynamic_cast<const OptionValue<T>*>(itr->second.get());
|
||||||
|
if (!option_value) {
|
||||||
|
throw Exception("Type mismatch: " + name);
|
||||||
|
}
|
||||||
|
return option_value->GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parse(int argc, const char* const* argv) {
|
||||||
|
if (argc < 1) {
|
||||||
|
errors_.push_back("No options");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
command_name_ = argv[0];
|
||||||
|
for (auto i = 1; i < argc; i++) {
|
||||||
|
const std::string arg(argv[i]);
|
||||||
|
|
||||||
|
// normal options: e.g. --bundle=/data/sample/bundle --fullscreen
|
||||||
|
if (arg.length() > 2 &&
|
||||||
|
arg.substr(0, 2).compare(kOptionStyleNormal) == 0) {
|
||||||
|
const size_t option_value_len = arg.find("=") != std::string::npos
|
||||||
|
? (arg.length() - arg.find("="))
|
||||||
|
: 0;
|
||||||
|
const bool has_value = option_value_len != 0;
|
||||||
|
std::string option_name =
|
||||||
|
arg.substr(2, arg.length() - 2 - option_value_len);
|
||||||
|
|
||||||
|
if (options_.find(option_name) == options_.end()) {
|
||||||
|
errors_.push_back("Not found option: " + option_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_value && options_[option_name]->IsRequiredValue()) {
|
||||||
|
errors_.push_back(option_name + " requres an option value");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_value && !options_[option_name]->IsRequiredValue()) {
|
||||||
|
errors_.push_back(option_name + " doesn't requres an option value");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_value) {
|
||||||
|
SetOptionValue(option_name, arg.substr(arg.find("=") + 1));
|
||||||
|
} else {
|
||||||
|
SetOption(option_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// short options: e.g. -f /foo/file.txt -h 640 -abc
|
||||||
|
else if (arg.length() > 1 &&
|
||||||
|
arg.substr(0, 1).compare(kOptionStyleShort) == 0) {
|
||||||
|
for (size_t j = 1; j < arg.length(); j++) {
|
||||||
|
const std::string option_name{argv[i][j]};
|
||||||
|
|
||||||
|
if (lut_short_options_.find(option_name) ==
|
||||||
|
lut_short_options_.end()) {
|
||||||
|
errors_.push_back("Not found short option: " + option_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j == arg.length() - 1 &&
|
||||||
|
options_[lut_short_options_[option_name]]->IsRequiredValue()) {
|
||||||
|
if (i == argc - 1) {
|
||||||
|
errors_.push_back("Invalid format option: " + option_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SetOptionValue(lut_short_options_[option_name], argv[++i]);
|
||||||
|
} else {
|
||||||
|
SetOption(lut_short_options_[option_name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errors_.push_back("Invalid format option: " + arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < registration_order_options_.size(); i++) {
|
||||||
|
if (registration_order_options_[i]->IsRequired() &&
|
||||||
|
!registration_order_options_[i]->HasValue()) {
|
||||||
|
errors_.push_back(
|
||||||
|
std::string(registration_order_options_[i]->GetName()) +
|
||||||
|
" option is mandatory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors_.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetError() { return errors_.size() > 0 ? errors_[0] : ""; }
|
||||||
|
|
||||||
|
std::vector<std::string>& GetErrors() { return errors_; }
|
||||||
|
|
||||||
|
std::string ShowHelp() {
|
||||||
|
std::ostringstream ostream;
|
||||||
|
|
||||||
|
ostream << "Usage: " << command_name_ << " ";
|
||||||
|
for (size_t i = 0; i < registration_order_options_.size(); i++) {
|
||||||
|
if (registration_order_options_[i]->IsRequired()) {
|
||||||
|
ostream << registration_order_options_[i]->GetHelpShortMessage() << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ostream << std::endl;
|
||||||
|
|
||||||
|
ostream << "Global options:" << std::endl;
|
||||||
|
size_t max_name_len = 0;
|
||||||
|
for (size_t i = 0; i < registration_order_options_.size(); i++) {
|
||||||
|
max_name_len = std::max(
|
||||||
|
max_name_len, registration_order_options_[i]->GetName().length());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < registration_order_options_.size(); i++) {
|
||||||
|
if (!registration_order_options_[i]->GetShortName().empty()) {
|
||||||
|
ostream << kOptionStyleShort
|
||||||
|
<< registration_order_options_[i]->GetShortName() << ", ";
|
||||||
|
} else {
|
||||||
|
ostream << std::string(4, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index_adjust = 0;
|
||||||
|
constexpr int kSpacerNum = 5;
|
||||||
|
auto need_value = registration_order_options_[i]->IsRequiredValue();
|
||||||
|
ostream << kOptionStyleNormal
|
||||||
|
<< registration_order_options_[i]->GetName();
|
||||||
|
if (need_value) {
|
||||||
|
ostream << kOptionValueForHelpMessage;
|
||||||
|
index_adjust += std::string(kOptionValueForHelpMessage).length();
|
||||||
|
}
|
||||||
|
ostream << std::string(
|
||||||
|
max_name_len + kSpacerNum - index_adjust -
|
||||||
|
registration_order_options_[i]->GetName().length(),
|
||||||
|
' ');
|
||||||
|
ostream << registration_order_options_[i]->GetDescription() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ostream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ReaderInt {
|
||||||
|
int operator()(const std::string& value) { return std::stoi(value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReaderString {
|
||||||
|
std::string operator()(const std::string& value) { return value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Option {
|
||||||
|
public:
|
||||||
|
Option(const std::string& name, const std::string& short_name,
|
||||||
|
const std::string& description, bool required, bool required_value)
|
||||||
|
: name_(name),
|
||||||
|
short_name_(short_name),
|
||||||
|
description_(description),
|
||||||
|
is_required_(required),
|
||||||
|
is_required_value_(required_value),
|
||||||
|
value_set_(false){};
|
||||||
|
virtual ~Option() = default;
|
||||||
|
|
||||||
|
const std::string& GetName() const { return name_; };
|
||||||
|
|
||||||
|
const std::string& GetShortName() const { return short_name_; };
|
||||||
|
|
||||||
|
const std::string& GetDescription() const { return description_; };
|
||||||
|
|
||||||
|
const std::string GetHelpShortMessage() const {
|
||||||
|
std::string message = kOptionStyleNormal + name_;
|
||||||
|
if (is_required_value_) {
|
||||||
|
message += kOptionValueForHelpMessage;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRequired() const { return is_required_; };
|
||||||
|
|
||||||
|
bool IsRequiredValue() const { return is_required_value_; };
|
||||||
|
|
||||||
|
void Set() { value_set_ = true; };
|
||||||
|
|
||||||
|
virtual bool SetValue(const std::string& value) = 0;
|
||||||
|
|
||||||
|
virtual bool HasValue() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string name_;
|
||||||
|
std::string short_name_;
|
||||||
|
std::string description_;
|
||||||
|
bool is_required_;
|
||||||
|
bool is_required_value_;
|
||||||
|
bool value_set_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class OptionValue : public Option {
|
||||||
|
public:
|
||||||
|
OptionValue(const std::string& name, const std::string& short_name,
|
||||||
|
const std::string& description, const T& default_value,
|
||||||
|
bool required, bool required_value)
|
||||||
|
: Option(name, short_name, description, required, required_value),
|
||||||
|
default_value_(default_value),
|
||||||
|
value_(default_value){};
|
||||||
|
virtual ~OptionValue() = default;
|
||||||
|
|
||||||
|
bool SetValue(const std::string& value) {
|
||||||
|
value_ = Read(value);
|
||||||
|
value_set_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasValue() const { return value_set_; }
|
||||||
|
|
||||||
|
const T& GetValue() const { return value_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual T Read(const std::string& s) = 0;
|
||||||
|
|
||||||
|
T default_value_;
|
||||||
|
T value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename F>
|
||||||
|
class OptionValueReader : public OptionValue<T> {
|
||||||
|
public:
|
||||||
|
OptionValueReader(const std::string& name, const std::string& short_name,
|
||||||
|
const std::string& description, const T default_value,
|
||||||
|
F reader, bool required, bool required_value)
|
||||||
|
: OptionValue<T>(name, short_name, description, default_value, required,
|
||||||
|
required_value),
|
||||||
|
reader_(reader) {}
|
||||||
|
~OptionValueReader() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
T Read(const std::string& value) { return reader_(value); }
|
||||||
|
|
||||||
|
F reader_;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool SetOption(const std::string& name) {
|
||||||
|
auto itr = options_.find(name);
|
||||||
|
if (itr == options_.end()) {
|
||||||
|
errors_.push_back("Unknown option: " + name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
itr->second->Set();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetOptionValue(const std::string& name, const std::string& value) {
|
||||||
|
auto itr = options_.find(name);
|
||||||
|
if (itr == options_.end()) {
|
||||||
|
errors_.push_back("Unknown option: " + name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!itr->second->SetValue(value)) {
|
||||||
|
errors_.push_back("Invalid option value: " + name + " = " + value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string command_name_;
|
||||||
|
std::unordered_map<std::string, std::unique_ptr<Option>> options_;
|
||||||
|
std::unordered_map<std::string, std::string> lut_short_options_;
|
||||||
|
std::vector<Option*> registration_order_options_;
|
||||||
|
std::vector<std::string> errors_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace commandline
|
||||||
|
|
||||||
|
#endif // COMMAND_OPTIONS_
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
// Copyright 2021 Sony Corporation. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_EMBEDDER_OPTIONS_
|
||||||
|
#define FLUTTER_EMBEDDER_OPTIONS_
|
||||||
|
|
||||||
|
#include <flutter/flutter_view_controller.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "command_options.h"
|
||||||
|
|
||||||
|
class FlutterEmbedderOptions {
|
||||||
|
public:
|
||||||
|
FlutterEmbedderOptions() {
|
||||||
|
options_.AddString("bundle", "b", "Path to Flutter app bundle", "./",
|
||||||
|
false);
|
||||||
|
options_.AddWithoutValue("no-cursor", "n", "No mouse cursor/pointer",
|
||||||
|
false);
|
||||||
|
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
|
||||||
|
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
|
||||||
|
// no more options.
|
||||||
|
#elif defined(FLUTTER_TARGET_BACKEND_X11)
|
||||||
|
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
|
||||||
|
false);
|
||||||
|
options_.AddInt("width", "w", "Flutter app window width", 1280, false);
|
||||||
|
options_.AddInt("height", "h", "Flutter app window height", 720, false);
|
||||||
|
#else // FLUTTER_TARGET_BACKEND_WAYLAND
|
||||||
|
options_.AddWithoutValue("onscreen-keyboard", "k",
|
||||||
|
"Enable on-screen keyboard", false);
|
||||||
|
options_.AddWithoutValue("window-decoration", "d",
|
||||||
|
"Enable window decorations", false);
|
||||||
|
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
|
||||||
|
false);
|
||||||
|
options_.AddInt("width", "w", "Flutter app window width", 1280, false);
|
||||||
|
options_.AddInt("height", "h", "Flutter app window height", 720, false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
~FlutterEmbedderOptions() = default;
|
||||||
|
|
||||||
|
bool Parse(int argc, char** argv) {
|
||||||
|
if (!options_.Parse(argc, argv)) {
|
||||||
|
std::cerr << options_.GetError() << std::endl;
|
||||||
|
std::cout << options_.ShowHelp();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle_path_ = options_.GetValue<std::string>("bundle");
|
||||||
|
use_mouse_cursor_ = !options_.Exist("no-cursor");
|
||||||
|
|
||||||
|
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
|
||||||
|
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
|
||||||
|
use_onscreen_keyboard_ = false;
|
||||||
|
use_window_decoration_ = false;
|
||||||
|
window_view_mode_ = flutter::FlutterViewController::ViewMode::kFullscreen;
|
||||||
|
#elif defined(FLUTTER_TARGET_BACKEND_X11)
|
||||||
|
use_onscreen_keyboard_ = false;
|
||||||
|
use_window_decoration_ = false;
|
||||||
|
window_view_mode_ =
|
||||||
|
options_.Exist("fullscreen")
|
||||||
|
? flutter::FlutterViewController::ViewMode::kFullscreen
|
||||||
|
: flutter::FlutterViewController::ViewMode::kNormal;
|
||||||
|
window_width_ = options_.GetValue<int>("width");
|
||||||
|
window_height_ = options_.GetValue<int>("height");
|
||||||
|
#else // FLUTTER_TARGET_BACKEND_WAYLAND
|
||||||
|
use_onscreen_keyboard_ = options_.Exist("onscreen-keyboard");
|
||||||
|
use_window_decoration_ = options_.Exist("window-decoration");
|
||||||
|
window_view_mode_ =
|
||||||
|
options_.Exist("fullscreen")
|
||||||
|
? flutter::FlutterViewController::ViewMode::kFullscreen
|
||||||
|
: flutter::FlutterViewController::ViewMode::kNormal;
|
||||||
|
window_width_ = options_.GetValue<int>("width");
|
||||||
|
window_height_ = options_.GetValue<int>("height");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BundlePath() const { return bundle_path_; }
|
||||||
|
bool IsUseMouseCursor() const { return use_mouse_cursor_; }
|
||||||
|
bool IsUseOnscreenKeyboard() const { return use_onscreen_keyboard_; }
|
||||||
|
bool IsUseWindowDecoraation() const { return use_window_decoration_; }
|
||||||
|
flutter::FlutterViewController::ViewMode WindowViewMode() const {
|
||||||
|
return window_view_mode_;
|
||||||
|
}
|
||||||
|
int WindowWidth() const { return window_width_; }
|
||||||
|
int WindowHeight() const { return window_height_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
commandline::CommandOptions options_;
|
||||||
|
|
||||||
|
std::string bundle_path_;
|
||||||
|
bool use_mouse_cursor_ = true;
|
||||||
|
bool use_onscreen_keyboard_ = false;
|
||||||
|
bool use_window_decoration_ = false;
|
||||||
|
flutter::FlutterViewController::ViewMode window_view_mode_ =
|
||||||
|
flutter::FlutterViewController::ViewMode::kNormal;
|
||||||
|
int window_width_ = 1280;
|
||||||
|
int window_height_ = 720;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FLUTTER_EMBEDDER_OPTIONS_
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2021 Sony Corporation. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "flutter_window.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "flutter/generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
FlutterWindow::FlutterWindow(
|
||||||
|
const flutter::FlutterViewController::ViewProperties view_properties,
|
||||||
|
const flutter::DartProject project)
|
||||||
|
: view_properties_(view_properties), project_(project) {}
|
||||||
|
|
||||||
|
bool FlutterWindow::OnCreate() {
|
||||||
|
flutter_view_controller_ = std::make_unique<flutter::FlutterViewController>(
|
||||||
|
view_properties_, project_);
|
||||||
|
|
||||||
|
// Ensure that basic setup of the controller was successful.
|
||||||
|
if (!flutter_view_controller_->engine() ||
|
||||||
|
!flutter_view_controller_->view()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register Flutter plugins.
|
||||||
|
RegisterPlugins(flutter_view_controller_->engine());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlutterWindow::OnDestroy() {
|
||||||
|
if (flutter_view_controller_) {
|
||||||
|
flutter_view_controller_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlutterWindow::Run() {
|
||||||
|
// Main loop.
|
||||||
|
auto next_flutter_event_time =
|
||||||
|
std::chrono::steady_clock::time_point::clock::now();
|
||||||
|
while (flutter_view_controller_->view()->DispatchEvent()) {
|
||||||
|
// Wait until the next event.
|
||||||
|
{
|
||||||
|
auto wait_duration =
|
||||||
|
std::max(std::chrono::nanoseconds(0),
|
||||||
|
next_flutter_event_time -
|
||||||
|
std::chrono::steady_clock::time_point::clock::now());
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(wait_duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processes any pending events in the Flutter engine, and returns the
|
||||||
|
// number of nanoseconds until the next scheduled event (or max, if none).
|
||||||
|
auto wait_duration = flutter_view_controller_->engine()->ProcessMessages();
|
||||||
|
{
|
||||||
|
auto next_event_time = std::chrono::steady_clock::time_point::max();
|
||||||
|
if (wait_duration != std::chrono::nanoseconds::max()) {
|
||||||
|
next_event_time =
|
||||||
|
std::min(next_event_time,
|
||||||
|
std::chrono::steady_clock::time_point::clock::now() +
|
||||||
|
wait_duration);
|
||||||
|
} else {
|
||||||
|
// Wait for the next frame if no events.
|
||||||
|
auto frame_rate = flutter_view_controller_->view()->GetFrameRate();
|
||||||
|
next_event_time = std::min(
|
||||||
|
next_event_time,
|
||||||
|
std::chrono::steady_clock::time_point::clock::now() +
|
||||||
|
std::chrono::milliseconds(
|
||||||
|
static_cast<int>(std::trunc(1000000.0 / frame_rate))));
|
||||||
|
}
|
||||||
|
next_flutter_event_time =
|
||||||
|
std::max(next_flutter_event_time, next_event_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2021 Sony Corporation. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_WINDOW_
|
||||||
|
#define FLUTTER_WINDOW_
|
||||||
|
|
||||||
|
#include <flutter/dart_project.h>
|
||||||
|
#include <flutter/flutter_view_controller.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class FlutterWindow {
|
||||||
|
public:
|
||||||
|
explicit FlutterWindow(
|
||||||
|
const flutter::FlutterViewController::ViewProperties view_properties,
|
||||||
|
const flutter::DartProject project);
|
||||||
|
~FlutterWindow() = default;
|
||||||
|
|
||||||
|
// Prevent copying.
|
||||||
|
FlutterWindow(FlutterWindow const&) = delete;
|
||||||
|
FlutterWindow& operator=(FlutterWindow const&) = delete;
|
||||||
|
|
||||||
|
bool OnCreate();
|
||||||
|
void OnDestroy();
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
flutter::FlutterViewController::ViewProperties view_properties_;
|
||||||
|
flutter::DartProject project_;
|
||||||
|
std::unique_ptr<flutter::FlutterViewController> flutter_view_controller_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FLUTTER_WINDOW_
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2021 Sony Corporation. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include <flutter/dart_project.h>
|
||||||
|
#include <flutter/flutter_view_controller.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "flutter_embedder_options.h"
|
||||||
|
#include "flutter_window.h"
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
FlutterEmbedderOptions options;
|
||||||
|
if (!options.Parse(argc, argv)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the Flutter project.
|
||||||
|
const auto bundle_path = options.BundlePath();
|
||||||
|
const std::wstring fl_path(bundle_path.begin(), bundle_path.end());
|
||||||
|
flutter::DartProject project(fl_path);
|
||||||
|
auto command_line_arguments = std::vector<std::string>();
|
||||||
|
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
|
||||||
|
|
||||||
|
flutter::FlutterViewController::ViewProperties view_properties = {};
|
||||||
|
view_properties.width = options.WindowWidth();
|
||||||
|
view_properties.height = options.WindowHeight();
|
||||||
|
view_properties.view_mode = options.WindowViewMode();
|
||||||
|
view_properties.use_mouse_cursor = options.IsUseMouseCursor();
|
||||||
|
view_properties.use_onscreen_keyboard = options.IsUseOnscreenKeyboard();
|
||||||
|
view_properties.use_window_decoration = options.IsUseWindowDecoraation();
|
||||||
|
|
||||||
|
// The Flutter instance hosted by this window.
|
||||||
|
FlutterWindow window(view_properties, project);
|
||||||
|
if (!window.OnCreate()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
window.Run();
|
||||||
|
window.OnDestroy();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
import 'package:shared_preferences_elinux/shared_preferences_elinux.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
group('SharedPreferencesELinux', () {
|
||||||
|
const Map<String, dynamic> kTestValues = <String, dynamic>{
|
||||||
|
'flutter.String': 'hello world',
|
||||||
|
'flutter.bool': true,
|
||||||
|
'flutter.int': 42,
|
||||||
|
'flutter.double': 3.14159,
|
||||||
|
'flutter.List': <String>['foo', 'bar'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const Map<String, dynamic> kTestValues2 = <String, dynamic>{
|
||||||
|
'flutter.String': 'goodbye world',
|
||||||
|
'flutter.bool': false,
|
||||||
|
'flutter.int': 1337,
|
||||||
|
'flutter.double': 2.71828,
|
||||||
|
'flutter.List': <String>['baz', 'quox'],
|
||||||
|
};
|
||||||
|
|
||||||
|
late SharedPreferencesELinux preferences;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
preferences = SharedPreferencesELinux.instance;
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
preferences.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('reading', (WidgetTester _) async {
|
||||||
|
final all = await preferences.getAll();
|
||||||
|
expect(all['String'], isNull);
|
||||||
|
expect(all['bool'], isNull);
|
||||||
|
expect(all['int'], isNull);
|
||||||
|
expect(all['double'], isNull);
|
||||||
|
expect(all['List'], isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('writing', (WidgetTester _) async {
|
||||||
|
await Future.wait(<Future<bool>>[
|
||||||
|
preferences.setValue(
|
||||||
|
'String', 'String', kTestValues2['flutter.String']),
|
||||||
|
preferences.setValue('Bool', 'bool', kTestValues2['flutter.bool']),
|
||||||
|
preferences.setValue('Int', 'int', kTestValues2['flutter.int']),
|
||||||
|
preferences.setValue(
|
||||||
|
'Double', 'double', kTestValues2['flutter.double']),
|
||||||
|
preferences.setValue('StringList', 'List', kTestValues2['flutter.List'])
|
||||||
|
]);
|
||||||
|
final all = await preferences.getAll();
|
||||||
|
expect(all['String'], kTestValues2['flutter.String']);
|
||||||
|
expect(all['bool'], kTestValues2['flutter.bool']);
|
||||||
|
expect(all['int'], kTestValues2['flutter.int']);
|
||||||
|
expect(all['double'], kTestValues2['flutter.double']);
|
||||||
|
expect(all['List'], kTestValues2['flutter.List']);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('removing', (WidgetTester _) async {
|
||||||
|
const String key = 'testKey';
|
||||||
|
|
||||||
|
await Future.wait([
|
||||||
|
preferences.setValue('String', key, kTestValues['flutter.String']),
|
||||||
|
preferences.setValue('Bool', key, kTestValues['flutter.bool']),
|
||||||
|
preferences.setValue('Int', key, kTestValues['flutter.int']),
|
||||||
|
preferences.setValue('Double', key, kTestValues['flutter.double']),
|
||||||
|
preferences.setValue('StringList', key, kTestValues['flutter.List'])
|
||||||
|
]);
|
||||||
|
await preferences.remove(key);
|
||||||
|
final all = await preferences.getAll();
|
||||||
|
expect(all['testKey'], isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('clearing', (WidgetTester _) async {
|
||||||
|
await Future.wait(<Future<bool>>[
|
||||||
|
preferences.setValue('String', 'String', kTestValues['flutter.String']),
|
||||||
|
preferences.setValue('Bool', 'bool', kTestValues['flutter.bool']),
|
||||||
|
preferences.setValue('Int', 'int', kTestValues['flutter.int']),
|
||||||
|
preferences.setValue('Double', 'double', kTestValues['flutter.double']),
|
||||||
|
preferences.setValue('StringList', 'List', kTestValues['flutter.List'])
|
||||||
|
]);
|
||||||
|
await preferences.clear();
|
||||||
|
final all = await preferences.getAll();
|
||||||
|
expect(all['String'], null);
|
||||||
|
expect(all['bool'], null);
|
||||||
|
expect(all['int'], null);
|
||||||
|
expect(all['double'], null);
|
||||||
|
expect(all['List'], null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2021 Sony Group Corporation. All rights reserved.
|
||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:shared_preferences_elinux/shared_preferences_elinux.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'SharedPreferences Demo',
|
||||||
|
home: SharedPreferencesDemo(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedPreferencesDemo extends StatefulWidget {
|
||||||
|
SharedPreferencesDemo({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
SharedPreferencesDemoState createState() => SharedPreferencesDemoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedPreferencesDemoState extends State<SharedPreferencesDemo> {
|
||||||
|
final prefs = SharedPreferencesELinux.instance;
|
||||||
|
late Future<int> _counter;
|
||||||
|
|
||||||
|
Future<void> _incrementCounter() async {
|
||||||
|
final values = await prefs.getAll();
|
||||||
|
final int counter = (values['counter'] as int? ?? 0) + 1;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_counter = prefs.setValue('Int', 'counter', counter).then((bool success) {
|
||||||
|
return counter;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_counter = prefs.getAll().then((Map<String, Object> values) {
|
||||||
|
return (values['counter'] as int? ?? 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text("SharedPreferences Demo"),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: FutureBuilder<int>(
|
||||||
|
future: _counter,
|
||||||
|
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
|
||||||
|
switch (snapshot.connectionState) {
|
||||||
|
case ConnectionState.waiting:
|
||||||
|
return const CircularProgressIndicator();
|
||||||
|
default:
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return Text('Error: ${snapshot.error}');
|
||||||
|
} else {
|
||||||
|
return Text(
|
||||||
|
'Button tapped ${snapshot.data} time${snapshot.data == 1 ? '' : 's'}.\n\n'
|
||||||
|
'This should persist across restarts.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: _incrementCounter,
|
||||||
|
tooltip: 'Increment',
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: shared_preferences_elinux_example
|
||||||
|
description: Demonstrates how to use the shared_preferences_elinux plugin.
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
|
flutter: ">=1.20.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
shared_preferences_elinux:
|
||||||
|
path: ../
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_driver:
|
||||||
|
sdk: flutter
|
||||||
|
integration_test:
|
||||||
|
sdk: flutter
|
||||||
|
pedantic: ^1.10.0
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:integration_test/integration_test_driver.dart';
|
||||||
|
|
||||||
|
Future<void> main() => integrationDriver();
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
// Copyright 2021 Sony Group Corporation. All rights reserved.
|
||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert' show json;
|
||||||
|
|
||||||
|
import 'package:file/file.dart';
|
||||||
|
import 'package:file/local.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:path_provider_elinux/path_provider_elinux.dart';
|
||||||
|
import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart';
|
||||||
|
|
||||||
|
/// The Linux implementation of [SharedPreferencesStorePlatform].
|
||||||
|
///
|
||||||
|
/// This class implements the `package:shared_preferences` functionality for Linux.
|
||||||
|
class SharedPreferencesELinux extends SharedPreferencesStorePlatform {
|
||||||
|
/// The default instance of [SharedPreferencesELinux] to use.
|
||||||
|
static SharedPreferencesELinux instance = SharedPreferencesELinux();
|
||||||
|
|
||||||
|
/// Registers the eLinux implementation.
|
||||||
|
static void registerWith() {
|
||||||
|
SharedPreferencesStorePlatform.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Local copy of preferences
|
||||||
|
Map<String, Object>? _cachedPreferences;
|
||||||
|
|
||||||
|
/// File system used to store to disk. Exposed for testing only.
|
||||||
|
@visibleForTesting
|
||||||
|
FileSystem fs = LocalFileSystem();
|
||||||
|
|
||||||
|
/// Gets the file where the preferences are stored.
|
||||||
|
Future<File?> _getLocalDataFile() async {
|
||||||
|
final pathProvider = PathProviderELinux();
|
||||||
|
final directory = await pathProvider.getApplicationSupportPath();
|
||||||
|
if (directory == null) return null;
|
||||||
|
return fs.file(path.join(directory, 'shared_preferences.json'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the preferences from the stored file. Once read, the preferences are
|
||||||
|
/// maintained in memory.
|
||||||
|
Future<Map<String, Object>> _readPreferences() async {
|
||||||
|
if (_cachedPreferences != null) {
|
||||||
|
return _cachedPreferences!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> preferences = {};
|
||||||
|
final File? localDataFile = await _getLocalDataFile();
|
||||||
|
if (localDataFile != null && localDataFile.existsSync()) {
|
||||||
|
String stringMap = localDataFile.readAsStringSync();
|
||||||
|
if (stringMap.isNotEmpty) {
|
||||||
|
preferences = json.decode(stringMap).cast<String, Object>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_cachedPreferences = preferences;
|
||||||
|
return preferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the cached preferences to disk. Returns [true] if the operation
|
||||||
|
/// succeeded.
|
||||||
|
Future<bool> _writePreferences(Map<String, Object> preferences) async {
|
||||||
|
try {
|
||||||
|
var localDataFile = await _getLocalDataFile();
|
||||||
|
if (localDataFile == null) {
|
||||||
|
print("Unable to determine where to write preferences.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!localDataFile.existsSync()) {
|
||||||
|
localDataFile.createSync(recursive: true);
|
||||||
|
}
|
||||||
|
var stringMap = json.encode(preferences);
|
||||||
|
localDataFile.writeAsStringSync(stringMap);
|
||||||
|
} catch (e) {
|
||||||
|
print("Error saving preferences to disk: $e");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> clear() async {
|
||||||
|
var preferences = await _readPreferences();
|
||||||
|
preferences.clear();
|
||||||
|
return _writePreferences(preferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Map<String, Object>> getAll() async {
|
||||||
|
return _readPreferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> remove(String key) async {
|
||||||
|
var preferences = await _readPreferences();
|
||||||
|
preferences.remove(key);
|
||||||
|
return _writePreferences(preferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> setValue(String valueType, String key, Object value) async {
|
||||||
|
var preferences = await _readPreferences();
|
||||||
|
preferences[key] = value;
|
||||||
|
return _writePreferences(preferences);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
name: shared_preferences_elinux
|
||||||
|
description: eLinux implementation of the shared_preferences plugin
|
||||||
|
homepage: https://github.com/sony/flutter-elinux-plugins
|
||||||
|
repository: https://github.com/sony/flutter-elinux-plugins/tree/main/packages/shared_preferences
|
||||||
|
version: 1.0.0
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
|
flutter: ">=2.0.0"
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
plugin:
|
||||||
|
implements: shared_preferences
|
||||||
|
platforms:
|
||||||
|
elinux:
|
||||||
|
dartPluginClass: SharedPreferencesELinux
|
||||||
|
pluginClass: none
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
file: ^6.0.0
|
||||||
|
meta: ^1.3.0
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
path: ^1.8.0
|
||||||
|
path_provider_elinux:
|
||||||
|
git:
|
||||||
|
url: https://github.com/sony/flutter-elinux-plugins.git
|
||||||
|
path: packages/path_provider
|
||||||
|
ref: main
|
||||||
|
shared_preferences_platform_interface: ^2.0.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
pedantic: ^1.10.0
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2021 Sony Group Corporation. All rights reserved.
|
||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
import 'package:file/memory.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:path_provider_elinux/path_provider_elinux.dart';
|
||||||
|
import 'package:shared_preferences_elinux/shared_preferences_elinux.dart';
|
||||||
|
import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late MemoryFileSystem fs;
|
||||||
|
|
||||||
|
SharedPreferencesELinux.registerWith();
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
fs = MemoryFileSystem.test();
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<String> _getFilePath() async {
|
||||||
|
final pathProvider = PathProviderLinux();
|
||||||
|
final directory = await pathProvider.getApplicationSupportPath();
|
||||||
|
return path.join(directory!, 'shared_preferences.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
_writeTestFile(String value) async {
|
||||||
|
fs.file(await _getFilePath())
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _readTestFile() async {
|
||||||
|
return fs.file(await _getFilePath()).readAsStringSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPreferencesLinux _getPreferences() {
|
||||||
|
var prefs = SharedPreferencesLinux();
|
||||||
|
prefs.fs = fs;
|
||||||
|
return prefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('registered instance', () {
|
||||||
|
expect(
|
||||||
|
SharedPreferencesStorePlatform.instance, isA<SharedPreferencesLinux>());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getAll', () async {
|
||||||
|
await _writeTestFile('{"key1": "one", "key2": 2}');
|
||||||
|
var prefs = _getPreferences();
|
||||||
|
|
||||||
|
var values = await prefs.getAll();
|
||||||
|
expect(values, hasLength(2));
|
||||||
|
expect(values['key1'], 'one');
|
||||||
|
expect(values['key2'], 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('remove', () async {
|
||||||
|
await _writeTestFile('{"key1":"one","key2":2}');
|
||||||
|
var prefs = _getPreferences();
|
||||||
|
|
||||||
|
await prefs.remove('key2');
|
||||||
|
|
||||||
|
expect(await _readTestFile(), '{"key1":"one"}');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setValue', () async {
|
||||||
|
await _writeTestFile('{}');
|
||||||
|
var prefs = _getPreferences();
|
||||||
|
|
||||||
|
await prefs.setValue('', 'key1', 'one');
|
||||||
|
await prefs.setValue('', 'key2', 2);
|
||||||
|
|
||||||
|
expect(await _readTestFile(), '{"key1":"one","key2":2}');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('clear', () async {
|
||||||
|
await _writeTestFile('{"key1":"one","key2":2}');
|
||||||
|
var prefs = _getPreferences();
|
||||||
|
|
||||||
|
await prefs.clear();
|
||||||
|
expect(await _readTestFile(), '{}');
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user