├── .gitignore ├── package_info ├── go.mod ├── import.go.tmpl ├── README.md ├── plugin.go └── go.sum ├── image_picker ├── go.mod ├── file_unsupported.go ├── import.go.tmpl ├── README.md ├── file_linux.go ├── file_darwin.go ├── plugin.go ├── file_windows.go └── go.sum ├── url_launcher ├── go.mod ├── import.go.tmpl ├── README.md ├── plugin.go └── go.sum ├── video_player ├── go.mod ├── import.go.tmpl ├── README.md ├── go.sum ├── plugin.go └── ffmpeg-video.go ├── shared_preferences ├── go.mod ├── import.go.tmpl ├── README.md ├── plugin.go └── go.sum ├── renovate.json ├── path_provider ├── go.mod ├── import.go.tmpl ├── README.md ├── plugin.go └── go.sum ├── list.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /package_info/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-flutter-desktop/plugins/package_info 2 | 3 | go 1.19 4 | 5 | require github.com/go-flutter-desktop/go-flutter v0.52.2 6 | -------------------------------------------------------------------------------- /image_picker/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-flutter-desktop/plugins/image_picker 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/go-flutter-desktop/go-flutter v0.52.2 7 | github.com/pkg/errors v0.9.1 8 | ) 9 | -------------------------------------------------------------------------------- /url_launcher/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-flutter-desktop/plugins/url_launcher 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/go-flutter-desktop/go-flutter v0.52.2 7 | github.com/pkg/errors v0.9.1 8 | ) 9 | -------------------------------------------------------------------------------- /video_player/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-flutter-desktop/plugins/video_player 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/3d0c/gmf 3c1e70196c59 7 | github.com/go-flutter-desktop/go-flutter v0.52.2 8 | ) 9 | -------------------------------------------------------------------------------- /shared_preferences/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-flutter-desktop/plugins/shared_preferences 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/go-flutter-desktop/go-flutter v0.52.2 7 | github.com/pkg/errors v0.9.1 8 | github.com/syndtr/goleveldb v1.0.0 9 | ) 10 | -------------------------------------------------------------------------------- /image_picker/file_unsupported.go: -------------------------------------------------------------------------------- 1 | // +build !darwin,!linux,!windows 2 | 3 | package image_picker 4 | 5 | import ( 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func fileDialog(title string, fileType string) (string, error) { 10 | return "", errors.New("platform unsupported") 11 | } 12 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "automerge": true, 4 | "major": { 5 | "automerge": false 6 | }, 7 | "gomodTidy": true, 8 | "requiredStatusChecks": null, 9 | "postUpdateOptions": [ 10 | "gomodTidy" 11 | ], 12 | "ignoreDeps": [ 13 | "golang.org/x/crypto", 14 | "golang.org/x/net", 15 | "golang.org/x/sys" 16 | ], 17 | "ignorePaths": [ 18 | "*/android/**" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /image_picker/import.go.tmpl: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // DO NOT EDIT, this file is generated by hover at compile-time for the image_picker plugin. 4 | 5 | import ( 6 | flutter "github.com/go-flutter-desktop/go-flutter" 7 | image_picker "github.com/go-flutter-desktop/plugins/image_picker" 8 | ) 9 | 10 | func init() { 11 | // Only the init function can be tweaked by plugin maker. 12 | options = append(options, flutter.AddPlugin(&image_picker.ImagePickerPlugin{})) 13 | } 14 | -------------------------------------------------------------------------------- /package_info/import.go.tmpl: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // DO NOT EDIT, this file is generated by hover at compile-time for the package_info plugin. 4 | 5 | import ( 6 | flutter "github.com/go-flutter-desktop/go-flutter" 7 | package_info "github.com/go-flutter-desktop/plugins/package_info" 8 | ) 9 | 10 | func init() { 11 | // Only the init function can be tweaked by plugin maker. 12 | options = append(options, flutter.AddPlugin(&package_info.PackageInfoPlugin{})) 13 | } 14 | -------------------------------------------------------------------------------- /url_launcher/import.go.tmpl: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // DO NOT EDIT, this file is generated by hover at compile-time for the url_launcher plugin. 4 | 5 | import ( 6 | flutter "github.com/go-flutter-desktop/go-flutter" 7 | url_launcher "github.com/go-flutter-desktop/plugins/url_launcher" 8 | ) 9 | 10 | func init() { 11 | // Only the init function can be tweaked by plugin maker. 12 | options = append(options, flutter.AddPlugin(&url_launcher.UrlLauncherPlugin{})) 13 | } 14 | -------------------------------------------------------------------------------- /path_provider/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-flutter-desktop/plugins/path_provider 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/adrg/xdg v0.5.3 7 | github.com/go-flutter-desktop/go-flutter v0.52.2 8 | github.com/pkg/errors v0.9.1 9 | ) 10 | 11 | require ( 12 | github.com/Xuanwo/go-locale v1.1.0 // indirect 13 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect 14 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f // indirect 15 | golang.org/x/sys v0.26.0 // indirect 16 | golang.org/x/text v0.3.7 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /path_provider/import.go.tmpl: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // DO NOT EDIT, this file is generated by hover at compile-time for the path_provider plugin. 4 | 5 | import ( 6 | flutter "github.com/go-flutter-desktop/go-flutter" 7 | path_provider "github.com/go-flutter-desktop/plugins/path_provider" 8 | ) 9 | 10 | func init() { 11 | // Only the init function can be tweaked by plugin maker. 12 | options = append(options, flutter.AddPlugin(&path_provider.PathProviderPlugin{ 13 | VendorName: flutter.ProjectOrganizationName, 14 | ApplicationName: flutter.ProjectName, 15 | })) 16 | } 17 | -------------------------------------------------------------------------------- /shared_preferences/import.go.tmpl: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // DO NOT EDIT, this file is generated by hover at compile-time for the shared_preferences plugin. 4 | 5 | import ( 6 | flutter "github.com/go-flutter-desktop/go-flutter" 7 | shared_preferences "github.com/go-flutter-desktop/plugins/shared_preferences" 8 | ) 9 | 10 | func init() { 11 | // Only the init function can be tweaked by plugin maker. 12 | options = append(options, flutter.AddPlugin(&shared_preferences.SharedPreferencesPlugin{ 13 | VendorName: flutter.ProjectOrganizationName, 14 | ApplicationName: flutter.ProjectName, 15 | })) 16 | } 17 | -------------------------------------------------------------------------------- /image_picker/README.md: -------------------------------------------------------------------------------- 1 | # image_picker 2 | 3 | This Go package implements the host-side of the Flutter [image_picker](https://github.com/flutter/plugins/tree/master/packages/image_picker) plugin. 4 | 5 | ## Usage 6 | 7 | Import as: 8 | 9 | ```go 10 | import "github.com/go-flutter-desktop/plugins/image_picker" 11 | ``` 12 | 13 | Then add the following option to your go-flutter [application options](https://github.com/go-flutter-desktop/go-flutter/wiki/Plugin-info): 14 | 15 | ```go 16 | flutter.AddPlugin(&image_picker.ImagePickerPlugin{}), 17 | ``` 18 | 19 | ## Issues 20 | 21 | Please report issues at the [go-flutter issue tracker](https://github.com/go-flutter-desktop/go-flutter/issues/). 22 | -------------------------------------------------------------------------------- /url_launcher/README.md: -------------------------------------------------------------------------------- 1 | # url_launcher 2 | 3 | This Go package implements the host-side of the Flutter [url_launcher](https://github.com/flutter/plugins/tree/master/packages/url_launcher) plugin. 4 | 5 | ## Usage 6 | 7 | Import as: 8 | 9 | ```go 10 | import "github.com/go-flutter-desktop/plugins/url_launcher" 11 | ``` 12 | 13 | Then add the following option to your go-flutter [application options](https://github.com/go-flutter-desktop/go-flutter/wiki/Plugin-info): 14 | 15 | ```go 16 | flutter.AddPlugin(&url_launcher.UrlLauncherPlugin{}), 17 | ``` 18 | 19 | ## Issues 20 | 21 | Please report issues at the [go-flutter issue tracker](https://github.com/go-flutter-desktop/go-flutter/issues/). 22 | -------------------------------------------------------------------------------- /video_player/import.go.tmpl: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // DO NOT EDIT, this file is generated by hover at compile-time for the video_player plugin. 4 | 5 | import ( 6 | "fmt" 7 | // flutter "github.com/go-flutter-desktop/go-flutter" 8 | // video_player "github.com/go-flutter-desktop/plugins/video_player" 9 | ) 10 | 11 | func init() { 12 | fmt.Println("Adding the VideoPlayerPlugin plugin to your project is risky!\nPlease read the plugin README!") 13 | fmt.Println("If you really want to give the video_player a try, uncomment the AddPlugin line in the import-video_player-plugin.go file located in go/cmd.") 14 | // options = append(options, flutter.AddPlugin(&video_player.VideoPlayerPlugin{})) 15 | } 16 | -------------------------------------------------------------------------------- /package_info/README.md: -------------------------------------------------------------------------------- 1 | # package_info 2 | 3 | This Go package implements the host-side of the Flutter [package_info](https://github.com/flutter/plugins/tree/master/packages/package_info) plugin. 4 | 5 | ## Usage 6 | 7 | Import as: 8 | 9 | ```go 10 | import "github.com/go-flutter-desktop/plugins/package_info" 11 | ``` 12 | 13 | Then add the following option to your go-flutter [application options](https://github.com/go-flutter-desktop/go-flutter/wiki/Plugin-info): 14 | 15 | ```go 16 | flutter.AddPlugin(&package_info.PackageInfoPlugin{}), 17 | ``` 18 | 19 | This plugin requires go-flutter `v0.30.0`, and the latest version of hover. 20 | 21 | ## Issues 22 | 23 | Please report issues at the [go-flutter issue tracker](https://github.com/go-flutter-desktop/go-flutter/issues/). 24 | -------------------------------------------------------------------------------- /list.json: -------------------------------------------------------------------------------- 1 | { 2 | "standaloneImplementation": [ 3 | {"name":"image_picker","importFile": "https://raw.githubusercontent.com/go-flutter-desktop/plugins/master/image_picker/import.go.tmpl"}, 4 | {"name":"path_provider","importFile": "https://raw.githubusercontent.com/go-flutter-desktop/plugins/master/path_provider/import.go.tmpl"}, 5 | {"name":"shared_preferences","importFile": "https://raw.githubusercontent.com/go-flutter-desktop/plugins/master/shared_preferences/import.go.tmpl"}, 6 | {"name":"url_launcher","importFile": "https://raw.githubusercontent.com/go-flutter-desktop/plugins/master/url_launcher/import.go.tmpl"}, 7 | {"name":"video_player","importFile": "https://raw.githubusercontent.com/go-flutter-desktop/plugins/master/video_player/import.go.tmpl"}, 8 | {"name":"package_info","importFile": "https://raw.githubusercontent.com/go-flutter-desktop/plugins/master/package_info/import.go.tmpl"} 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /path_provider/README.md: -------------------------------------------------------------------------------- 1 | # path_provider 2 | 3 | This Go package implements the host-side of the Flutter [path_provider](https://github.com/flutter/plugins/tree/master/packages/path_provider) plugin. 4 | 5 | ## Usage 6 | 7 | Import as: 8 | 9 | ```go 10 | import "github.com/go-flutter-desktop/plugins/path_provider" 11 | ``` 12 | 13 | Then add the following option to your go-flutter [application options](https://github.com/go-flutter-desktop/go-flutter/wiki/Plugin-info): 14 | 15 | ```go 16 | flutter.AddPlugin(&path_provider.PathProviderPlugin{ 17 | VendorName: "myOrganizationOrUsername", 18 | ApplicationName: "myApplicationName", 19 | }), 20 | ``` 21 | 22 | Change the values of the Vendor and Application names to a custom and unique 23 | string, so it doesn't conflict with other organizations. 24 | 25 | ## Issues 26 | 27 | Please report issues at the [go-flutter issue tracker](https://github.com/go-flutter-desktop/go-flutter/issues/). 28 | -------------------------------------------------------------------------------- /shared_preferences/README.md: -------------------------------------------------------------------------------- 1 | # shared_preferences 2 | 3 | This Go package implements the host-side of the Flutter [shared_preferences](https://github.com/flutter/plugins/tree/master/packages/shared_preferences) plugin. 4 | 5 | ## Usage 6 | 7 | Import as: 8 | 9 | ```go 10 | import "github.com/go-flutter-desktop/plugins/shared_preferences" 11 | ``` 12 | 13 | Then add the following option to your go-flutter [application options](https://github.com/go-flutter-desktop/go-flutter/wiki/Plugin-info): 14 | 15 | ```go 16 | flutter.AddPlugin(&shared_preferences.SharedPreferencesPlugin{ 17 | VendorName: "myOrganizationOrUsername", 18 | ApplicationName: "myApplicationName", 19 | }) 20 | ``` 21 | 22 | Change the values of the Vendor and Application names to a custom and unique 23 | string, so it doesn't conflict with other organizations. 24 | 25 | ## Issues 26 | 27 | Please report issues at the [go-flutter issue tracker](https://github.com/go-flutter-desktop/go-flutter/issues/). 28 | -------------------------------------------------------------------------------- /image_picker/file_linux.go: -------------------------------------------------------------------------------- 1 | package image_picker 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "strings" 7 | 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | func fileDialog(title string, fileType string) (string, error) { 12 | cmd, err := exec.LookPath("zenity") 13 | if err != nil { 14 | return "", err 15 | } 16 | 17 | var filters string 18 | switch fileType { 19 | case "image": 20 | filters = `*.png *.jpg *.jpeg` 21 | case "video": 22 | filters = `*.webm *.mpeg *.mkv *.mp4 *.avi *.mov *.flv` 23 | default: 24 | return "", errors.New("unsupported fileType") 25 | } 26 | 27 | output, err := exec.Command(cmd, "--file-selection", "--title", title, "--file-filter="+filters).Output() 28 | if err != nil { 29 | if exitError, ok := err.(*exec.ExitError); ok { 30 | fmt.Printf("go-flutter/plugins/image_picker: file dialog exited with code %d and output `%s`\n", exitError.ExitCode(), string(output)) 31 | return "", nil // user probably canceled or closed the selection window 32 | } 33 | return "", errors.Wrap(err, "failed to open file dialog") 34 | } 35 | 36 | path := strings.TrimSpace(string(output)) 37 | return path, nil 38 | } 39 | -------------------------------------------------------------------------------- /package_info/plugin.go: -------------------------------------------------------------------------------- 1 | package package_info 2 | 3 | import ( 4 | flutter "github.com/go-flutter-desktop/go-flutter" 5 | "github.com/go-flutter-desktop/go-flutter/plugin" 6 | ) 7 | 8 | var channelNames = []string{"plugins.flutter.io/package_info", "dev.fluttercommunity.plus/package_info"} 9 | 10 | // PackageInfoPlugin implements flutter.Plugin and handles method calls to 11 | // the plugins.flutter.io/package_info channel. 12 | type PackageInfoPlugin struct{} 13 | 14 | var _ flutter.Plugin = &PackageInfoPlugin{} // compile-time type check 15 | 16 | // InitPlugin initializes the plugin. 17 | func (p *PackageInfoPlugin) InitPlugin(messenger plugin.BinaryMessenger) error { 18 | for _, channelName := range channelNames { 19 | channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{}) 20 | channel.HandleFunc("getAll", p.handlePackageInfo) 21 | } 22 | return nil 23 | } 24 | 25 | func (p *PackageInfoPlugin) handlePackageInfo(arguments interface{}) (reply interface{}, err error) { 26 | return map[interface{}]interface{}{ 27 | "appName": flutter.ProjectName, 28 | "packageName": flutter.ProjectOrganizationName + "." + flutter.ProjectName, 29 | "version": flutter.ProjectVersion, 30 | "buildNumber": flutter.ProjectVersion, 31 | }, nil 32 | } 33 | -------------------------------------------------------------------------------- /image_picker/file_darwin.go: -------------------------------------------------------------------------------- 1 | package image_picker 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | func fileDialog(title string, fileType string) (string, error) { 13 | osascript, err := exec.LookPath("osascript") 14 | if err != nil { 15 | return "", err 16 | } 17 | 18 | var filters string 19 | switch fileType { 20 | case "image": 21 | filters = `"PNG", "public.png", "JPEG", "jpg", "public.jpeg"` 22 | case "video": 23 | filters = `"MOV","mov","MP4","mp4","AVI","avi","MKV","mkv"` 24 | default: 25 | return "", errors.New("unsupported fileType") 26 | } 27 | 28 | output, err := exec.Command(osascript, "-e", `choose file of type {`+filters+`} with prompt "`+title+`"`).Output() 29 | if err != nil { 30 | if exitError, ok := err.(*exec.ExitError); ok { 31 | fmt.Printf("go-flutter/plugins/image_picker: file dialog exited with code %d and output `%s`\n", exitError.ExitCode(), string(output)) 32 | return "", nil // user probably canceled or closed the selection window 33 | } 34 | return "", errors.Wrap(err, "failed to open file dialog") 35 | } 36 | 37 | trimmedOutput := strings.TrimSpace(string(output)) 38 | 39 | pathParts := strings.Split(trimmedOutput, ":") 40 | path := string(filepath.Separator) + filepath.Join(pathParts[1:]...) 41 | return path, nil 42 | } 43 | -------------------------------------------------------------------------------- /video_player/README.md: -------------------------------------------------------------------------------- 1 | # video_player 2 | 3 | This Go package implements the host-side of the Flutter [video_player](https://github.com/flutter/plugins/tree/master/packages/video_player) plugin. 4 | 5 | ## Usage 6 | 7 | Import as: 8 | 9 | ```go 10 | import "github.com/go-flutter-desktop/plugins/video_player" 11 | ``` 12 | 13 | Then add the following option to your go-flutter [application options](https://github.com/go-flutter-desktop/go-flutter/wiki/Plugin-info): 14 | 15 | ```go 16 | flutter.AddPlugin(&video_player.VideoPlayerPlugin{}), 17 | ``` 18 | 19 | The plugin uses a third party library to handle video to image decoding, 20 | [3d0c/gmf](https://github.com/3d0c/gmf), a go 21 | FFmpeg bindings. 22 | If you have trouble installing the plugin, checkout their [installation](https://github.com/3d0c/gmf#installation) procedure. 23 | 24 | ## :warning: Disclaimer :warning: 25 | 26 | This plugin is available for educational purposes, and the go-flutter team isn't 27 | actively working on it. 28 | **`Don't use it in production`** nasty bugs can occur 29 | (mostly memory leak). 30 | The plugin needs a significant rewrite. We are looking for maintainers. Pull Requests are most welcome! 31 | 32 | If you get errors with this plugin, before reporting an issue, please check if your system works with the [video-to-goImage](https://github.com/3d0c/gmf/blob/f4b5acb7db5cbbda9a6209be1d0de5f552823f62/examples/video-to-goImage.go) gmf example. 33 | 34 | ## Issues 35 | 36 | Please report issues in the [go-flutter **video_player** issue tracker :warning:](https://github.com/go-flutter-desktop/go-flutter/issues/134). 37 | -------------------------------------------------------------------------------- /url_launcher/plugin.go: -------------------------------------------------------------------------------- 1 | package url_launcher 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "runtime" 7 | 8 | "github.com/go-flutter-desktop/go-flutter" 9 | "github.com/go-flutter-desktop/go-flutter/plugin" 10 | "github.com/pkg/errors" 11 | ) 12 | 13 | var channelNames = []string{ 14 | "plugins.flutter.io/url_launcher", 15 | "plugins.flutter.io/url_launcher_macos", 16 | "plugins.flutter.io/url_launcher_windows", 17 | "plugins.flutter.io/url_launcher_linux", 18 | } 19 | 20 | // ImagePickerPlugin implements flutter.Plugin and handles method calls to 21 | // the plugins.flutter.io/url_launcher channel. 22 | type UrlLauncherPlugin struct{} 23 | 24 | var _ flutter.Plugin = &UrlLauncherPlugin{} // compile-time type check 25 | 26 | // InitPlugin initializes the plugin. 27 | func (p *UrlLauncherPlugin) InitPlugin(messenger plugin.BinaryMessenger) error { 28 | for _, channelName := range channelNames { 29 | channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{}) 30 | channel.HandleFunc("launch", p.launch) 31 | channel.HandleFunc("canLaunch", p.canLaunch) 32 | 33 | // Ignored: The plugins doesn't handle WebView. 34 | // This call will not do anything, because there is no WebView to close. 35 | channel.HandleFunc("closeWebView", func(_ interface{}) (interface{}, error) { return nil, nil }) 36 | } 37 | return nil 38 | } 39 | 40 | func (p *UrlLauncherPlugin) launch(arguments interface{}) (reply interface{}, err error) { 41 | argsMap := arguments.(map[interface{}]interface{}) 42 | 43 | url := argsMap["url"].(string) 44 | if url == "" { 45 | return nil, errors.New("url is empty") 46 | } 47 | 48 | useWebView, ok := argsMap["useWebView"].(bool) 49 | if ok && useWebView == true { 50 | fmt.Println("go-flutter-desktop/plugins/url_launcher: WebView aren't supported on desktop.") 51 | } 52 | 53 | switch runtime.GOOS { 54 | case "linux": 55 | err = exec.Command("xdg-open", url).Start() 56 | case "windows": 57 | err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() 58 | case "darwin": 59 | err = exec.Command("open", url).Start() 60 | default: 61 | err = errors.New("Unsupported platform") 62 | } 63 | 64 | return err == nil, err 65 | } 66 | 67 | func (p *UrlLauncherPlugin) canLaunch(arguments interface{}) (reply interface{}, err error) { 68 | var url string 69 | 70 | argsMap := arguments.(map[interface{}]interface{}) 71 | url = argsMap["url"].(string) 72 | if url == "" { 73 | return false, nil 74 | } 75 | 76 | return true, nil 77 | } 78 | -------------------------------------------------------------------------------- /image_picker/plugin.go: -------------------------------------------------------------------------------- 1 | package image_picker 2 | 3 | import ( 4 | flutter "github.com/go-flutter-desktop/go-flutter" 5 | "github.com/go-flutter-desktop/go-flutter/plugin" 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | const channelName = "plugins.flutter.io/image_picker" 10 | 11 | const ( 12 | methodCallImage = `pickImage` 13 | methodCallVideo = `pickVideo` 14 | methodCallRetrieve = `retrieve` 15 | 16 | sourceCamera = 0 17 | sourceGallery = 1 18 | ) 19 | 20 | // ImagePickerPlugin implements flutter.Plugin and handles method calls to 21 | // the plugins.flutter.io/image_picker channel. 22 | type ImagePickerPlugin struct{} 23 | 24 | var _ flutter.Plugin = &ImagePickerPlugin{} // compile-time type check 25 | 26 | // InitPlugin initializes the path provider plugin. 27 | func (p *ImagePickerPlugin) InitPlugin(messenger plugin.BinaryMessenger) error { 28 | channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{}) 29 | channel.HandleFunc("pickImage", p.handlePickImage) 30 | channel.HandleFunc("pickVideo", p.handlePickVideo) 31 | channel.HandleFunc("retrieve", p.handleRetrieve) 32 | return nil 33 | } 34 | 35 | func (p *ImagePickerPlugin) handlePickImage(arguments interface{}) (reply interface{}, err error) { 36 | argsMap := arguments.(map[interface{}]interface{}) 37 | switch argsMap["source"].(int32) { 38 | case sourceCamera: 39 | return nil, errors.New("source camera is not yet supported by image_picker desktop plugin") 40 | case sourceGallery: 41 | if argsMap["maxWidth"] != nil || argsMap["maxHeight"] != nil { 42 | return nil, errors.New("maxWidth and maxHeight are not yet supported by image_picker desktop plugin") 43 | } 44 | path, err := fileDialog("Select an image", "image") 45 | if err != nil { 46 | return nil, errors.Wrap(err, "failed to pick an image") 47 | } 48 | if path == "" { 49 | return nil, nil 50 | } 51 | return path, nil 52 | } 53 | return 54 | } 55 | 56 | func (p *ImagePickerPlugin) handlePickVideo(arguments interface{}) (reply interface{}, err error) { 57 | argsMap := arguments.(map[interface{}]interface{}) 58 | switch argsMap["source"].(int32) { 59 | case sourceCamera: 60 | return nil, errors.New("source camera is not yet supported by image_picker desktop plugin") 61 | case sourceGallery: 62 | path, err := fileDialog("Select a video", "video") 63 | if err != nil { 64 | return nil, errors.Wrap(err, "failed to pick a video") 65 | } 66 | if path == "" { 67 | return nil, nil 68 | } 69 | return path, nil 70 | } 71 | return 72 | } 73 | 74 | func (p *ImagePickerPlugin) handleRetrieve(arguments interface{}) (reply interface{}, err error) { 75 | // retrieve is an android-only plugin method 76 | return nil, errors.New("retrieve is not supported by desktop image_picker plugin") 77 | } 78 | -------------------------------------------------------------------------------- /image_picker/file_windows.go: -------------------------------------------------------------------------------- 1 | package image_picker 2 | 3 | import ( 4 | "strings" 5 | "syscall" 6 | "unicode/utf16" 7 | "unsafe" 8 | 9 | "github.com/pkg/errors" 10 | ) 11 | 12 | var ( 13 | comdlg32 = syscall.NewLazyDLL("comdlg32.dll") 14 | getOpenFileNameW = comdlg32.NewProc("GetOpenFileNameW") 15 | ) 16 | 17 | const ( 18 | maxPath = 260 19 | ofnExplorer = 0x00080000 20 | ofnFileMustExist = 0x00001000 21 | ofnHideReadOnly = 0x00000004 22 | ) 23 | 24 | type openfilenameW struct { 25 | lStructSize uint32 26 | hwndOwner syscall.Handle 27 | hInstance syscall.Handle 28 | lpstrFilter *uint16 29 | lpstrCustomFilter *uint16 30 | nMaxCustFilter uint32 31 | nFilterIndex uint32 32 | lpstrFile *uint16 33 | nMaxFile uint32 34 | lpstrFileTitle *uint16 35 | nMaxFileTitle uint32 36 | lpstrInitialDir *uint16 37 | lpstrTitle *uint16 38 | flags uint32 39 | nFileOffset uint16 40 | nFileExtension uint16 41 | lpstrDefExt *uint16 42 | lCustData uintptr 43 | lpfnHook syscall.Handle 44 | lpTemplateName *uint16 45 | pvReserved unsafe.Pointer 46 | dwReserved uint32 47 | flagsEx uint32 48 | } 49 | 50 | func utf16PtrFromString(s string) *uint16 { 51 | b := utf16.Encode([]rune(s)) 52 | return &b[0] 53 | } 54 | 55 | func stringFromUtf16Ptr(p *uint16) string { 56 | b := *(*[maxPath]uint16)(unsafe.Pointer(p)) 57 | r := utf16.Decode(b[:]) 58 | return strings.Trim(string(r), "\x00") 59 | } 60 | 61 | func getOpenFileName(lpofn *openfilenameW) bool { 62 | ret, _, _ := getOpenFileNameW.Call(uintptr(unsafe.Pointer(lpofn)), 0, 0) 63 | return ret != 0 64 | } 65 | 66 | func fileDialog(title string, fileType string) (string, error) { 67 | var ofn openfilenameW 68 | buf := make([]uint16, maxPath) 69 | 70 | t, _ := syscall.UTF16PtrFromString(title) 71 | 72 | ofn.lStructSize = uint32(unsafe.Sizeof(ofn)) 73 | ofn.lpstrTitle = t 74 | ofn.lpstrFile = &buf[0] 75 | ofn.nMaxFile = uint32(len(buf)) 76 | 77 | var filters string 78 | switch fileType { 79 | case "image": 80 | filters = "Images (*.jpeg,*.png,*.gif)\x00*.jpg;*.jpeg;*.png;*.gif\x00All Files (*.*)\x00*.*\x00\x00" 81 | case "video": 82 | filters = "Videos (*.webm,*.wmv,*.mpeg,*.mkv,*.mp4,*.avi,*.mov,*.flv)\x00*.webm;*.wmv;*.mpeg;*.mkv;*mp4;*.avi;*.mov;*.flv\x00All Files (*.*)\x00*.*\x00\x00" 83 | default: 84 | return "", errors.New("unsupported fileType") 85 | } 86 | 87 | if filters != "" { 88 | ofn.lpstrFilter = utf16PtrFromString(filters) 89 | } 90 | 91 | flags := ofnExplorer | ofnFileMustExist | ofnHideReadOnly 92 | 93 | ofn.flags = uint32(flags) 94 | 95 | if getOpenFileName(&ofn) { 96 | return stringFromUtf16Ptr(ofn.lpstrFile), nil 97 | } 98 | 99 | return "", nil 100 | 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter desktop plugins 2 | 3 | This repo contains [go-flutter](https://github.com/go-flutter-desktop/go-flutter) implementations for popular flutter plugins. 4 | 5 | ## Issues 6 | 7 | Please report issues at the [go-flutter issue tracker](https://github.com/go-flutter-desktop/go-flutter/issues/). 8 | 9 | ## From [flutter/plugins](https://github.com/flutter/plugins) 10 | 11 | - [image_picker](image_picker) - Select an image or video from storage. ([pub.dev](https://pub.dev/packages/image_picker)) 12 | - [path_provider](path_provider) - Finding commonly used locations on the filesystem. ([pub.dev](https://pub.dev/packages/path_provider)) 13 | - [package_info](package_info) - Provides information about an application package. ([pub.dev](https://pub.dev/packages/package_info)) 14 | - [shared_preferences](shared_preferences) - Provides a persistent store for simple data. ([pub.dev](https://pub.dev/packages/shared_preferences)) 15 | - [url_launcher](url_launcher) - Flutter plugin for launching a URL. ([pub.dev](https://pub.dev/packages/url_launcher)) 16 | - [video_player](video_player) - Flutter plugin for playing back video on a Widget surface. ([pub.dev](https://pub.dev/packages/video_player)) (:warning: work-in-progress, needs rewrite) 17 | 18 | 19 | ## From the community 20 | - [file_picker](https://github.com/miguelpruivo/flutter_file_picker) - Select single or multiple file paths using the native file explorer. ([pub.dev](https://pub.dev/packages/file_picker)) 21 | - [sqlite](https://github.com/boltomli/go-flutter-plugin-sqlite) - Flutter plugin for SQLite. ([pub.dev](https://pub.dev/packages/sqflite)) 22 | - [platform_device_id](https://github.com/BestBurning/platform_device_id) - Query device identifier. ([pub.dev](https://pub.dev/packages/platform_device_id)) 23 | - [shutdown_platform](https://github.com/BestBurning/shutdown_platform) - Shutdown the machine. ([pub.dev](https://pub.dev/packages/shutdown_platform)) 24 | - [fast_rsa](https://github.com/jerson/flutter-rsa) - RSA for flutter made with golang for fast performance. ([pub.dev](https://pub.dev/packages/fast_rsa)) 25 | - [desktop_cursor](https://github.com/Luukdegram/desktop_cursor) - A Flutter desktop plugin to set the shape of the cursor. ([pub.dev](https://pub.dev/packages/desktop_cursor)) 26 | - [title_bar](https://github.com/zephylac/title_bar) - Support custom title bar color [go-flutter#177](https://github.com/go-flutter-desktop/go-flutter/issues/177). **Only for osx** 27 | - [clipboard_manager](https://github.com/djpnewton/go_flutter_clipboard_manager) - Flutter plugin for copying text to the clipboard. ([pub.dev](https://pub.dev/packages/clipboard_manager)) 28 | - [open_file](https://github.com/jld3103/go-flutter-open_file) - Flutter plugin for opening a file or URI using the default application on the platform. ([pub.dev](https://pub.dev/packages/open_file)) 29 | - [firebase_remote_config](https://github.com/jWinterDay/firebase_remote_config) - Flutter plugin for reading firebase remote config ([pub.dev](https://pub.dev/packages/firebase_remote_config)) 30 | - [warble](https://github.com/jslater89/warble) - Play audio from assets, files, or in-memory buffers. ([pub.dev](https://pub.dev/packages/warble)) 31 | - [systray](https://github.com/sonr-io/systray) - Support for systray menu for desktop flutter apps 32 | - [flutter_image_compress](https://github.com/OpenFlutter/flutter_image_compress) - Compresses image with fast native libraries 33 | 34 | If you have implemented a plugin that you would like to add to this repository, 35 | feel free to open a PR. 36 | -------------------------------------------------------------------------------- /path_provider/plugin.go: -------------------------------------------------------------------------------- 1 | package path_provider 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/adrg/xdg" 7 | 8 | "github.com/pkg/errors" 9 | 10 | flutter "github.com/go-flutter-desktop/go-flutter" 11 | "github.com/go-flutter-desktop/go-flutter/plugin" 12 | ) 13 | 14 | var channelNames = []string{ 15 | "plugins.flutter.io/path_provider", 16 | "plugins.flutter.io/path_provider_macos", 17 | } 18 | 19 | // PathProviderPlugin implements flutter.Plugin and handles method calls to 20 | // the plugins.flutter.io/path_provider channel. 21 | type PathProviderPlugin struct { 22 | // VendorName must be set to a nonempty value. Use company name or a domain 23 | // that you own. Note that the value must be valid as a cross-platform directory name. 24 | VendorName string 25 | // ApplicationName must be set to a nonempty value. Use the unique name for 26 | // this application. Note that the value must be valid as a cross-platform 27 | // directory name. 28 | ApplicationName string 29 | } 30 | 31 | var _ flutter.Plugin = &PathProviderPlugin{} // compile-time type check 32 | 33 | // InitPlugin initializes the path provider plugin. 34 | func (p *PathProviderPlugin) InitPlugin(messenger plugin.BinaryMessenger) error { 35 | if p.VendorName == "" { 36 | // returned immediately because this is likely a programming error 37 | return errors.New("PathProviderPlugin.VendorName must be set") 38 | } 39 | if p.ApplicationName == "" { 40 | // returned immediately because this is likely a programming error 41 | return errors.New("PathProviderPlugin.ApplicationName must be set") 42 | } 43 | 44 | for _, channelName := range channelNames { 45 | channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{}) 46 | channel.HandleFunc("getTemporaryDirectory", p.handleTempDir) 47 | channel.HandleFunc("getApplicationSupportDirectory", p.handleAppSupportDir) 48 | channel.HandleFunc("getLibraryDirectory", p.handleLibraryDir) // MacOS only 49 | channel.HandleFunc("getApplicationDocumentsDirectory", p.handleAppDocumentsDir) 50 | channel.HandleFunc("getStorageDirectory", p.returnError) // Android only 51 | channel.HandleFunc("getExternalCacheDirectories", p.returnError) // Android only 52 | channel.HandleFunc("getExternalStorageDirectories", p.returnError) // Android only 53 | channel.HandleFunc("getDownloadsDirectory", p.handleDownloadsDir) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | func (p *PathProviderPlugin) returnError(arguments interface{}) (reply interface{}, err error) { 60 | return nil, errors.New("This channel is not supported") 61 | } 62 | 63 | func (p *PathProviderPlugin) handleTempDir(arguments interface{}) (reply interface{}, err error) { 64 | return filepath.Join(xdg.CacheHome, p.VendorName, p.ApplicationName), nil 65 | } 66 | 67 | func (p *PathProviderPlugin) handleAppSupportDir(arguments interface{}) (reply interface{}, err error) { 68 | return filepath.Join(xdg.DataHome, p.VendorName, p.ApplicationName), nil 69 | } 70 | 71 | // handleLibraryDir is MacOS only and therefore hardcoded, as it is not specified in the XDG specifications 72 | func (p *PathProviderPlugin) handleLibraryDir(arguments interface{}) (reply interface{}, err error) { 73 | return "/Library/", nil 74 | } 75 | 76 | func (p *PathProviderPlugin) handleAppDocumentsDir(arguments interface{}) (reply interface{}, err error) { 77 | return filepath.Join(xdg.ConfigHome, p.VendorName, p.ApplicationName), nil 78 | } 79 | 80 | // handleDownloadsDir is from the flutter plugin side MacOS only 81 | // (https://github.com/flutter/plugins/blob/8819b219c5ca83a000ae482b9a51b7f1f421845b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart#L82) 82 | // but should work out of the box once the restriction is not longer there 83 | func (p *PathProviderPlugin) handleDownloadsDir(arguments interface{}) (reply interface{}, err error) { 84 | return xdg.UserDirs.Download, nil 85 | } 86 | -------------------------------------------------------------------------------- /image_picker/go.sum: -------------------------------------------------------------------------------- 1 | github.com/Xuanwo/go-locale v1.1.0 h1:51gUxhxl66oXAjI9uPGb2O0qwPECpriKQb2hl35mQkg= 2 | github.com/Xuanwo/go-locale v1.1.0/go.mod h1:UKrHoZB3FPIk9wIG2/tVSobnHgNnceGSH3Y8DY5cASs= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/go-flutter-desktop/go-flutter v0.52.2 h1:08LdijXBSiqUcSzofrytBPpItF+zBgJ7s0sDixFhbAU= 7 | github.com/go-flutter-desktop/go-flutter v0.52.2/go.mod h1:8lIXoHEAZl01qK7pKwUwf/dzF2mD0JRoaxkGoYu32k8= 8 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= 9 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 10 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f h1:w3h343WgVLKLITcSpwecCDcq0FO8pAv6A/UG86hhFtY= 11 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 12 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 13 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 14 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 15 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 16 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 17 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 18 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 19 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 20 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 21 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 22 | github.com/smartystreets/goconvey v1.6.7 h1:I6tZjLXD2Q1kjvNbIzB1wvQBsXmKXiVrhpRE8ZjP5jY= 23 | github.com/smartystreets/goconvey v1.6.7/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 24 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 25 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 26 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 27 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 28 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 29 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 30 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 31 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 32 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 33 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA= 34 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 36 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 37 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 38 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 39 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 40 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 41 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 42 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 43 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 44 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 45 | -------------------------------------------------------------------------------- /package_info/go.sum: -------------------------------------------------------------------------------- 1 | github.com/Xuanwo/go-locale v1.1.0 h1:51gUxhxl66oXAjI9uPGb2O0qwPECpriKQb2hl35mQkg= 2 | github.com/Xuanwo/go-locale v1.1.0/go.mod h1:UKrHoZB3FPIk9wIG2/tVSobnHgNnceGSH3Y8DY5cASs= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/go-flutter-desktop/go-flutter v0.52.2 h1:08LdijXBSiqUcSzofrytBPpItF+zBgJ7s0sDixFhbAU= 7 | github.com/go-flutter-desktop/go-flutter v0.52.2/go.mod h1:8lIXoHEAZl01qK7pKwUwf/dzF2mD0JRoaxkGoYu32k8= 8 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= 9 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 10 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f h1:w3h343WgVLKLITcSpwecCDcq0FO8pAv6A/UG86hhFtY= 11 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 12 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 13 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 14 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 15 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 16 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 17 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 18 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 19 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 20 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 21 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 22 | github.com/smartystreets/goconvey v1.6.7 h1:I6tZjLXD2Q1kjvNbIzB1wvQBsXmKXiVrhpRE8ZjP5jY= 23 | github.com/smartystreets/goconvey v1.6.7/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 24 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 25 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 26 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 27 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 28 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 29 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 30 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 31 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 32 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 33 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA= 34 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 36 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 37 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 38 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 39 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 40 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 41 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 42 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 43 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 44 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 45 | -------------------------------------------------------------------------------- /url_launcher/go.sum: -------------------------------------------------------------------------------- 1 | github.com/Xuanwo/go-locale v1.1.0 h1:51gUxhxl66oXAjI9uPGb2O0qwPECpriKQb2hl35mQkg= 2 | github.com/Xuanwo/go-locale v1.1.0/go.mod h1:UKrHoZB3FPIk9wIG2/tVSobnHgNnceGSH3Y8DY5cASs= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/go-flutter-desktop/go-flutter v0.52.2 h1:08LdijXBSiqUcSzofrytBPpItF+zBgJ7s0sDixFhbAU= 7 | github.com/go-flutter-desktop/go-flutter v0.52.2/go.mod h1:8lIXoHEAZl01qK7pKwUwf/dzF2mD0JRoaxkGoYu32k8= 8 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= 9 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 10 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f h1:w3h343WgVLKLITcSpwecCDcq0FO8pAv6A/UG86hhFtY= 11 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 12 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 13 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 14 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 15 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 16 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 17 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 18 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 19 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 20 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 21 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 22 | github.com/smartystreets/goconvey v1.6.7 h1:I6tZjLXD2Q1kjvNbIzB1wvQBsXmKXiVrhpRE8ZjP5jY= 23 | github.com/smartystreets/goconvey v1.6.7/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 24 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 25 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 26 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 27 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 28 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 29 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 30 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 31 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 32 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 33 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA= 34 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 36 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 37 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 38 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 39 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 40 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 41 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 42 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 43 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 44 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 45 | -------------------------------------------------------------------------------- /path_provider/go.sum: -------------------------------------------------------------------------------- 1 | github.com/Xuanwo/go-locale v1.1.0 h1:51gUxhxl66oXAjI9uPGb2O0qwPECpriKQb2hl35mQkg= 2 | github.com/Xuanwo/go-locale v1.1.0/go.mod h1:UKrHoZB3FPIk9wIG2/tVSobnHgNnceGSH3Y8DY5cASs= 3 | github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= 4 | github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/go-flutter-desktop/go-flutter v0.52.2 h1:08LdijXBSiqUcSzofrytBPpItF+zBgJ7s0sDixFhbAU= 9 | github.com/go-flutter-desktop/go-flutter v0.52.2/go.mod h1:8lIXoHEAZl01qK7pKwUwf/dzF2mD0JRoaxkGoYu32k8= 10 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= 11 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 12 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f h1:w3h343WgVLKLITcSpwecCDcq0FO8pAv6A/UG86hhFtY= 13 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 14 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 15 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 16 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 17 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 18 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 19 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 20 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 21 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 22 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 23 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 24 | github.com/smartystreets/goconvey v1.6.7 h1:I6tZjLXD2Q1kjvNbIzB1wvQBsXmKXiVrhpRE8ZjP5jY= 25 | github.com/smartystreets/goconvey v1.6.7/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 26 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 27 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 28 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 29 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 30 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 31 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 32 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 33 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 34 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 35 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 36 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 37 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 38 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 39 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 40 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 41 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 42 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 43 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 44 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 45 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 46 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 47 | -------------------------------------------------------------------------------- /video_player/go.sum: -------------------------------------------------------------------------------- 1 | github.com/3d0c/gmf v0.0.0-20220425074253-5646e6e80daf h1:rb71vCiYe4xIzE79j/D+SQiHmRpOGen7YTnLQbdxvTU= 2 | github.com/3d0c/gmf v0.0.0-20220425074253-5646e6e80daf/go.mod h1:PqcBsVCdnbbM6CMlSSPQMtmUyMfxF+Zjddh957qOzMw= 3 | github.com/Xuanwo/go-locale v1.1.0 h1:51gUxhxl66oXAjI9uPGb2O0qwPECpriKQb2hl35mQkg= 4 | github.com/Xuanwo/go-locale v1.1.0/go.mod h1:UKrHoZB3FPIk9wIG2/tVSobnHgNnceGSH3Y8DY5cASs= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/go-flutter-desktop/go-flutter v0.52.2 h1:08LdijXBSiqUcSzofrytBPpItF+zBgJ7s0sDixFhbAU= 9 | github.com/go-flutter-desktop/go-flutter v0.52.2/go.mod h1:8lIXoHEAZl01qK7pKwUwf/dzF2mD0JRoaxkGoYu32k8= 10 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= 11 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 12 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f h1:w3h343WgVLKLITcSpwecCDcq0FO8pAv6A/UG86hhFtY= 13 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 14 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 15 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 16 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 17 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 18 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 19 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 20 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 21 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 22 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 23 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 24 | github.com/smartystreets/goconvey v1.6.7 h1:I6tZjLXD2Q1kjvNbIzB1wvQBsXmKXiVrhpRE8ZjP5jY= 25 | github.com/smartystreets/goconvey v1.6.7/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 26 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 27 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 28 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 29 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 30 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 31 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 32 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 33 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 34 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 35 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA= 36 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 37 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 38 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 39 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 40 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 41 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 42 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 43 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 44 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 45 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 46 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 47 | -------------------------------------------------------------------------------- /shared_preferences/plugin.go: -------------------------------------------------------------------------------- 1 | package shared_preferences 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | flutter "github.com/go-flutter-desktop/go-flutter" 8 | "github.com/go-flutter-desktop/go-flutter/plugin" 9 | "github.com/pkg/errors" 10 | "github.com/syndtr/goleveldb/leveldb" 11 | "github.com/syndtr/goleveldb/leveldb/opt" 12 | ) 13 | 14 | const channelName = "plugins.flutter.io/shared_preferences" 15 | 16 | // SharedPreferencesPlugin implements flutter.Plugin and handles method calls to 17 | // the plugins.flutter.io/shared_preferences channel. Preferences are stored 18 | // using leveldb in the users' home directory config location. 19 | type SharedPreferencesPlugin struct { 20 | // VendorName must be set to a nonempty value. Use company name or a domain 21 | // that you own. Note that the value must be valid as a cross-platform directory name. 22 | VendorName string 23 | // ApplicationName must be set to a nonempty value. Use the unique name for 24 | // this application. Note that the value must be valid as a cross-platform 25 | // directory name. 26 | ApplicationName string 27 | 28 | userConfigFolder string 29 | db *leveldb.DB 30 | codec plugin.StandardMessageCodec 31 | } 32 | 33 | var _ flutter.Plugin = &SharedPreferencesPlugin{} // compile-time type check 34 | 35 | // InitPlugin initializes the shared preferences plugin. 36 | func (p *SharedPreferencesPlugin) InitPlugin(messenger plugin.BinaryMessenger) error { 37 | if p.VendorName == "" { 38 | // returned immediately because this is likely a programming error 39 | return errors.New("SharedPreferencesPlugin.VendorName must be set") 40 | } 41 | if p.ApplicationName == "" { 42 | // returned immediately because this is likely a programming error 43 | return errors.New("SharedPreferencesPlugin.ApplicationName must be set") 44 | } 45 | 46 | var err error 47 | p.userConfigFolder, err = os.UserConfigDir() 48 | if err != nil { 49 | return errors.Wrap(err, "failed to resolve user config dir") 50 | } 51 | 52 | // TODO: move into a getDB call which initializes on first use, lower startup latency. 53 | p.db, err = leveldb.OpenFile(filepath.Join(p.userConfigFolder, p.VendorName, p.ApplicationName, "shared_preferences.leveldb"), nil) 54 | if err != nil { 55 | // TODO: when moved into getDB: error shouldn't kill the plugin and thereby the whole app, 56 | return errors.Wrap(err, "failed to open leveldb for shared_preferences") 57 | } 58 | 59 | channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{}) 60 | channel.HandleFunc("setBool", p.handleSet) 61 | channel.HandleFunc("setDouble", p.handleSet) 62 | channel.HandleFunc("setInt", p.handleSet) 63 | channel.HandleFunc("setString", p.handleSet) 64 | channel.HandleFunc("setStringList", p.handleSet) 65 | channel.HandleFunc("commit", p.handleCommit) 66 | channel.HandleFunc("getAll", p.handleGetAll) 67 | channel.HandleFunc("remove", p.handleRemove) 68 | channel.HandleFunc("clear", p.handleClear) 69 | 70 | return nil 71 | } 72 | 73 | var defaultWriteOptions = &opt.WriteOptions{ 74 | Sync: true, 75 | } 76 | 77 | func (p *SharedPreferencesPlugin) handleSet(arguments interface{}) (reply interface{}, err error) { 78 | key := []byte(arguments.(map[interface{}]interface{})["key"].(string)) 79 | value, err := p.codec.EncodeMessage(arguments.(map[interface{}]interface{})["value"]) 80 | if err != nil { 81 | return false, errors.Wrap(err, "failed to encode value") 82 | } 83 | err = p.db.Put(key, value, defaultWriteOptions) 84 | if err != nil { 85 | return false, errors.Wrap(err, "failed to put key/value into db") 86 | } 87 | return true, nil 88 | } 89 | 90 | func (p *SharedPreferencesPlugin) handleCommit(arguments interface{}) (reply interface{}, err error) { 91 | // We've been committing the whole time. 92 | return true, nil 93 | } 94 | 95 | func (p *SharedPreferencesPlugin) handleGetAll(arguments interface{}) (reply interface{}, err error) { 96 | var values = make(map[interface{}]interface{}) 97 | iter := p.db.NewIterator(nil, nil) 98 | for iter.Next() { 99 | value, err := p.codec.DecodeMessage(iter.Value()) 100 | if err != nil { 101 | return nil, errors.Wrap(err, "failed to get value from db") 102 | } 103 | values[string(iter.Key())] = value 104 | } 105 | iter.Release() 106 | err = iter.Error() 107 | if err != nil { 108 | return nil, errors.Wrap(err, "failed to iterate over key/values in db") 109 | } 110 | return values, nil 111 | } 112 | 113 | func (p *SharedPreferencesPlugin) handleRemove(arguments interface{}) (reply interface{}, err error) { 114 | key := arguments.(map[interface{}]interface{})["key"].(string) 115 | err = p.db.Delete([]byte(key), nil) 116 | if err != nil { 117 | return nil, errors.Wrap(err, "failed to delete key/value from db") 118 | } 119 | return true, nil 120 | } 121 | 122 | func (p *SharedPreferencesPlugin) handleClear(arguments interface{}) (reply interface{}, err error) { 123 | iter := p.db.NewIterator(nil, nil) 124 | for iter.Next() { 125 | err = p.db.Delete(iter.Key(), defaultWriteOptions) 126 | if err != nil { 127 | return nil, errors.Wrap(err, "failed to delete key/value from db") 128 | } 129 | } 130 | iter.Release() 131 | err = iter.Error() 132 | if err != nil { 133 | return nil, errors.Wrap(err, "failed to iterate over key/values in db") 134 | } 135 | return true, nil 136 | } 137 | -------------------------------------------------------------------------------- /shared_preferences/go.sum: -------------------------------------------------------------------------------- 1 | github.com/Xuanwo/go-locale v1.1.0 h1:51gUxhxl66oXAjI9uPGb2O0qwPECpriKQb2hl35mQkg= 2 | github.com/Xuanwo/go-locale v1.1.0/go.mod h1:UKrHoZB3FPIk9wIG2/tVSobnHgNnceGSH3Y8DY5cASs= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 7 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 8 | github.com/go-flutter-desktop/go-flutter v0.52.2 h1:08LdijXBSiqUcSzofrytBPpItF+zBgJ7s0sDixFhbAU= 9 | github.com/go-flutter-desktop/go-flutter v0.52.2/go.mod h1:8lIXoHEAZl01qK7pKwUwf/dzF2mD0JRoaxkGoYu32k8= 10 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= 11 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 12 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f h1:w3h343WgVLKLITcSpwecCDcq0FO8pAv6A/UG86hhFtY= 13 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220712193148-63cf1f4ef61f/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 14 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 15 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 16 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= 17 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 18 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 19 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 20 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 21 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 22 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 23 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 24 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 25 | github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= 26 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 27 | github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= 28 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 29 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 30 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 31 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 32 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 33 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 34 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 35 | github.com/smartystreets/goconvey v1.6.7 h1:I6tZjLXD2Q1kjvNbIzB1wvQBsXmKXiVrhpRE8ZjP5jY= 36 | github.com/smartystreets/goconvey v1.6.7/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 37 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 38 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 39 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 40 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 41 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 42 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 43 | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= 44 | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= 45 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 46 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 47 | golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= 48 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 49 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= 50 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 51 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 52 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 53 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA= 54 | golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 55 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 56 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 57 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 58 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 59 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 60 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 61 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 62 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= 63 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 64 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 65 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 66 | gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= 67 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 68 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 69 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 70 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 71 | -------------------------------------------------------------------------------- /video_player/plugin.go: -------------------------------------------------------------------------------- 1 | package video_player 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | flutter "github.com/go-flutter-desktop/go-flutter" 9 | "github.com/go-flutter-desktop/go-flutter/plugin" 10 | ) 11 | 12 | const channelName = "flutter.io/videoPlayer" 13 | 14 | // VideoPlayerPlugin implements flutter.Plugin and handles the host side of 15 | // the official Dart Video Player plugin for Flutter. 16 | // VideoPlayerPlugin contains multiple players. 17 | type VideoPlayerPlugin struct { 18 | textureRegistry *flutter.TextureRegistry 19 | messenger plugin.BinaryMessenger 20 | videoPlayers map[int32]*player 21 | } 22 | 23 | var _ flutter.Plugin = &VideoPlayerPlugin{} // compile-time type check 24 | 25 | // InitPlugin initializes the plugin. 26 | func (p *VideoPlayerPlugin) InitPlugin(messenger plugin.BinaryMessenger) error { 27 | p.messenger = messenger 28 | p.videoPlayers = make(map[int32]*player) 29 | channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{}) 30 | channel.HandleFunc("init", func(_ interface{}) (interface{}, error) { return nil, nil }) 31 | channel.HandleFunc("create", p.create) 32 | channel.HandleFunc("play", p.play) 33 | channel.HandleFunc("pause", p.pause) 34 | channel.HandleFunc("position", p.position) 35 | channel.HandleFunc("dispose", p.dispose) 36 | channel.CatchAllHandleFunc(warning) 37 | return nil 38 | } 39 | 40 | func warning(methodCall interface{}) (interface{}, error) { 41 | method := methodCall.(plugin.MethodCall) 42 | fmt.Println("go-flutter/plugins/video_player WARNING MethodCall to '", 43 | method.Method, "' isn't supported by the Golang video_player", 44 | "\n -- please refer to: github.com/go-flutter-desktop/go-flutter/issues/134", 45 | "for more information.") 46 | return nil, nil 47 | } 48 | 49 | // InitPluginTexture is used to create and manage backend textures 50 | func (p *VideoPlayerPlugin) InitPluginTexture(registry *flutter.TextureRegistry) error { 51 | p.textureRegistry = registry 52 | return nil 53 | } 54 | 55 | func (p *VideoPlayerPlugin) create(arguments interface{}) (reply interface{}, err error) { 56 | args := arguments.(map[interface{}]interface{}) 57 | 58 | if _, ok := args["asset"]; ok { 59 | return nil, errors.New("only online video and relative path videos are supported") 60 | } 61 | texture := p.textureRegistry.NewTexture() 62 | 63 | player := &player{ 64 | uri: args["uri"].(string), 65 | texture: texture, 66 | } 67 | 68 | eventChannel := plugin.NewEventChannel(p.messenger, fmt.Sprintf("flutter.io/videoPlayer/videoEvents%d", texture.ID), plugin.StandardMethodCodec{}) 69 | eventChannel.Handle(player) 70 | 71 | p.videoPlayers[int32(texture.ID)] = player 72 | texture.Register(player.textureHanler) 73 | 74 | return map[interface{}]interface{}{ 75 | "textureId": texture.ID, 76 | }, nil 77 | } 78 | 79 | func (p *VideoPlayerPlugin) play(arguments interface{}) (reply interface{}, err error) { 80 | args := arguments.(map[interface{}]interface{}) 81 | p.videoPlayers[args["textureId"].(int32)].play() 82 | return nil, nil 83 | } 84 | 85 | func (p *VideoPlayerPlugin) dispose(arguments interface{}) (reply interface{}, err error) { 86 | args := arguments.(map[interface{}]interface{}) 87 | p.videoPlayers[args["textureId"].(int32)].dispose() 88 | return nil, nil 89 | } 90 | 91 | func (p *VideoPlayerPlugin) pause(arguments interface{}) (reply interface{}, err error) { 92 | args := arguments.(map[interface{}]interface{}) 93 | player := p.videoPlayers[args["textureId"].(int32)] 94 | player.videoBuffer.Pause() 95 | return nil, nil 96 | } 97 | 98 | func (p *VideoPlayerPlugin) position(arguments interface{}) (reply interface{}, err error) { 99 | args := arguments.(map[interface{}]interface{}) 100 | videoPlayer := p.videoPlayers[args["textureId"].(int32)] 101 | 102 | return int64(videoPlayer.currentTime * 1000), nil 103 | } 104 | 105 | // player correspond to one instance of a player with his associated texture 106 | // handler, eventChannel, methodChannel handler 107 | type player struct { 108 | uri string 109 | videoBuffer *ffmpegVideo 110 | texture flutter.Texture 111 | eventSink *plugin.EventSink 112 | 113 | // Keep the frame in-sync 114 | newFrame chan bool 115 | 116 | isStreaming bool 117 | currentTime float64 118 | } 119 | 120 | func (p *player) OnListen(arguments interface{}, sink *plugin.EventSink) { // flutter.EventChannel interface 121 | p.eventSink = sink 122 | 123 | p.newFrame = make(chan bool, 2) 124 | p.videoBuffer = &ffmpegVideo{} 125 | 126 | bufferSize := 10 // in frames 127 | 128 | err := p.videoBuffer.Init(p.uri, bufferSize) 129 | if err != nil { 130 | sink.Error("VideoError", fmt.Sprintf("Video player had error: %v", err), nil) 131 | } 132 | 133 | vWidth, vHeight := p.videoBuffer.Bounds() 134 | sink.Success(map[interface{}]interface{}{ 135 | "event": "initialized", 136 | "duration": int64(p.videoBuffer.Duration() * 1000), 137 | "width": int32(vWidth), 138 | "height": int32(vHeight), 139 | }) 140 | sink.EndOfStream() // !not a complete implementation 141 | } 142 | func (p *player) OnCancel(arguments interface{}) {} // flutter.EventChannel interface 143 | 144 | func (p *player) dispose() { 145 | p.videoBuffer.Cancel() 146 | p.texture.UnRegister() 147 | for len(p.videoBuffer.Frames) > 0 { 148 | pixels := <-p.videoBuffer.Frames // get the frame, ! Block the main thread ! 149 | pixels.Free() 150 | } 151 | } 152 | 153 | func (p *player) play() { 154 | if p.isStreaming { 155 | p.videoBuffer.UnPause() 156 | return 157 | } 158 | p.isStreaming = true 159 | 160 | consumer := func() { 161 | imagePerSec := p.videoBuffer.GetFrameRate() 162 | 163 | for p.videoBuffer.HasFrameAvailable() { 164 | p.videoBuffer.WaitUnPause() 165 | time.Sleep(time.Duration(imagePerSec*1000) * time.Millisecond) 166 | p.videoBuffer.WaitUnPause() 167 | p.newFrame <- true 168 | p.texture.FrameAvailable() // trigger p.textureHanler (display new frame) 169 | } 170 | } 171 | 172 | // on the pending frames, consume image in the channel 173 | go func() { 174 | p.videoBuffer.Stream(consumer) 175 | close(p.newFrame) 176 | p.videoBuffer.Free() 177 | }() 178 | 179 | } 180 | 181 | func (p *player) textureHanler(width, height int) (bool, *flutter.PixelBuffer) { 182 | if p.videoBuffer.Closed() { 183 | return false, nil 184 | } 185 | 186 | // Sync frames 187 | select { 188 | case <-p.newFrame: 189 | default: 190 | // Drop this frame, the event doesn't come from this plugin 191 | return false, nil 192 | } 193 | 194 | vWidth, vHeight := p.videoBuffer.Bounds() 195 | pixels := <-p.videoBuffer.Frames // get the frame, ! Block the main thread ! 196 | p.currentTime = pixels.Time() 197 | defer pixels.Free() 198 | return true, &flutter.PixelBuffer{ // send the image to the scene 199 | Pix: pixels.Data(), 200 | Width: vWidth, 201 | Height: vHeight, 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /video_player/ffmpeg-video.go: -------------------------------------------------------------------------------- 1 | package video_player 2 | 3 | // This file hides the ffmpeg related computations. 4 | // 5 | // The api exposed by github.com/3d0c/gmf is messy (all go-ffmpeg lib are) 6 | // which explain some of the following mess :). 7 | // 8 | // Based on the examples 'video-to-goImage.go' of 3d0c/gmf. 9 | // TODO: fix the memory leak will occur.. 10 | import ( 11 | "errors" 12 | "fmt" 13 | "io" 14 | "sync" 15 | "sync/atomic" 16 | 17 | "github.com/3d0c/gmf" 18 | ) 19 | 20 | type ffmpegVideo struct { 21 | swsctx *gmf.SwsCtx 22 | ist *gmf.Stream 23 | cc *gmf.CodecCtx 24 | inputCtx *gmf.FmtCtx 25 | srcVideoStream *gmf.Stream 26 | Frames chan *ffmpegFrame 27 | paused chan bool 28 | player *playerStatus 29 | pausedFlag bool 30 | width int 31 | height int 32 | } 33 | 34 | type ffmpegFrame struct { 35 | // ffmpeg packet 36 | packet *gmf.Packet 37 | // in second 38 | time float64 39 | } 40 | 41 | func (f *ffmpegFrame) Time() float64 { 42 | return f.time 43 | } 44 | 45 | func (f *ffmpegFrame) Data() []byte { 46 | return f.packet.Data() 47 | } 48 | 49 | func (f *ffmpegFrame) Free() { 50 | f.packet.Free() 51 | } 52 | 53 | type playerStatus struct{ flag int32 } 54 | 55 | const ( 56 | allImagesProcessed = 2 57 | playing = 1 58 | noImagesAvailable = 0 59 | ) 60 | 61 | func (f *ffmpegVideo) Init(srcFileName string, bufferSize int) (err error) { 62 | f.player = new(playerStatus) 63 | f.paused = make(chan bool) 64 | 65 | f.inputCtx, err = gmf.NewInputCtx(srcFileName) 66 | f.Frames = make(chan *ffmpegFrame, bufferSize) 67 | 68 | f.srcVideoStream, err = f.inputCtx.GetBestStream(gmf.AVMEDIA_TYPE_VIDEO) 69 | if err != nil { 70 | return errors.New("No video stream found in " + srcFileName + "\n") 71 | } 72 | 73 | codec, err := gmf.FindEncoder(gmf.AV_CODEC_ID_RAWVIDEO) 74 | if err != nil { 75 | return err 76 | } 77 | 78 | f.cc = gmf.NewCodecCtx(codec) 79 | 80 | f.cc. 81 | SetTimeBase(gmf.AVR{Num: 1, Den: 1}). 82 | SetPixFmt(gmf.AV_PIX_FMT_RGBA). 83 | SetWidth(f.srcVideoStream.CodecCtx().Width()). 84 | SetHeight(f.srcVideoStream.CodecCtx().Height()) 85 | 86 | f.width, f.height = f.cc.Width(), f.cc.Height() 87 | 88 | if codec.IsExperimental() { 89 | f.cc.SetStrictCompliance(gmf.FF_COMPLIANCE_EXPERIMENTAL) 90 | } 91 | 92 | if err := f.cc.Open(nil); err != nil { 93 | return err 94 | } 95 | 96 | f.ist, err = f.inputCtx.GetStream(f.srcVideoStream.Index()) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | // convert source pix_fmt into AV_PIX_FMT_RGBA 102 | // which is set up by codec context above 103 | icc := f.srcVideoStream.CodecCtx() 104 | if f.swsctx, err = gmf.NewSwsCtx(icc.Width(), icc.Height(), icc.PixFmt(), f.cc.Width(), f.cc.Height(), f.cc.PixFmt(), gmf.SWS_BICUBIC); err != nil { 105 | return err 106 | } 107 | 108 | return nil 109 | } 110 | 111 | func (f *ffmpegVideo) Free() { 112 | f.Cancel() 113 | f.srcVideoStream.Free() 114 | f.swsctx.Free() 115 | f.ist.Free() 116 | f.inputCtx.Free() 117 | gmf.Release(f.cc) 118 | f.cc.Free() 119 | close(f.Frames) 120 | } 121 | 122 | func (f *ffmpegVideo) Stream(onFirstFrame func()) { 123 | drain := -1 124 | hasConsumer := false 125 | var wg sync.WaitGroup 126 | 127 | defer func() { 128 | if r := recover(); r != nil { 129 | fmt.Println("go-flutter/plugins/video_player: recover: ", r) 130 | for len(f.Frames) > 0 { // clean the frame channel 131 | pixels := <-f.Frames 132 | defer pixels.Free() 133 | } 134 | f.Cancel() 135 | } 136 | }() 137 | 138 | for { 139 | if drain >= 0 { 140 | break 141 | } 142 | 143 | pkt, err := f.inputCtx.GetNextPacket() 144 | if err != nil && err != io.EOF { 145 | if pkt != nil { 146 | pkt.Free() 147 | } 148 | fmt.Printf("go-flutter/plugins/video_player: error getting next packet - %s\n", err) 149 | break 150 | } else if err != nil && pkt == nil { 151 | drain = 0 152 | } 153 | 154 | if pkt != nil && pkt.StreamIndex() != f.srcVideoStream.Index() { 155 | continue 156 | } 157 | 158 | frames, err := f.ist.CodecCtx().Decode(pkt) 159 | if err != nil { 160 | fmt.Printf("go-flutter/plugins/video_player: Fatal error during decoding - %s\n", err) 161 | break 162 | } 163 | 164 | // Decode() method doesn't treat EAGAIN and EOF as errors 165 | // it returns empty frames slice instead. Countinue until 166 | // input EOF or frames received. 167 | if len(frames) == 0 && drain < 0 { 168 | continue 169 | } 170 | 171 | if frames, err = gmf.DefaultRescaler(f.swsctx, frames); err != nil { 172 | panic(err) 173 | } 174 | 175 | packets, err := f.cc.Encode(frames, drain) 176 | if err != nil { 177 | fmt.Printf("go-flutter/plugins/video_player: Error encoding - %s\n", err) 178 | panic(err) 179 | } 180 | 181 | for _, p := range packets { 182 | 183 | if f.Closed() && hasConsumer { 184 | break 185 | } 186 | 187 | timebase := f.srcVideoStream.TimeBase() 188 | time := float64(timebase.AVR().Num) / float64(timebase.AVR().Den) * float64(p.Pts()) 189 | f.Frames <- &ffmpegFrame{packet: p, time: time} 190 | if !hasConsumer { 191 | f.play() 192 | wg.Add(1) 193 | go func() { 194 | onFirstFrame() 195 | wg.Done() 196 | }() 197 | hasConsumer = true 198 | } 199 | 200 | } 201 | 202 | for i := range frames { 203 | frames[i].Free() 204 | } 205 | 206 | if pkt != nil { 207 | pkt.Free() 208 | pkt = nil 209 | } 210 | } 211 | if !f.Closed() { 212 | f.EndOfVideo() 213 | } 214 | for i := 0; i < f.inputCtx.StreamsCnt(); i++ { 215 | st, _ := f.inputCtx.GetStream(i) 216 | defer st.CodecCtx().Free() 217 | defer st.Free() 218 | } 219 | 220 | wg.Wait() 221 | } 222 | 223 | func (f *ffmpegVideo) Bounds() (int, int) { 224 | return f.width, f.height 225 | } 226 | 227 | func (f *ffmpegVideo) GetFrameRate() float64 { 228 | a := f.srcVideoStream.GetRFrameRate().AVR() 229 | return float64(a.Den) / float64(a.Num) 230 | } 231 | 232 | func (f *ffmpegVideo) Duration() float64 { 233 | return f.inputCtx.Duration() 234 | } 235 | 236 | func (f *ffmpegVideo) EndOfVideo() { 237 | f.Set(allImagesProcessed) 238 | } 239 | 240 | func (f *ffmpegVideo) play() { 241 | f.Set(playing) 242 | } 243 | 244 | func (f *ffmpegVideo) Pause() { 245 | f.pausedFlag = true 246 | } 247 | 248 | func (f *ffmpegVideo) UnPause() { 249 | f.pausedFlag = false 250 | f.paused <- true 251 | } 252 | 253 | func (f *ffmpegVideo) Cancel() { 254 | f.Set(noImagesAvailable) 255 | } 256 | 257 | func (f *ffmpegVideo) Set(value int32) { 258 | atomic.StoreInt32(&(f.player.flag), value) 259 | } 260 | 261 | func (f *ffmpegVideo) Closed() bool { 262 | flag := atomic.LoadInt32(&(f.player.flag)) 263 | if flag == allImagesProcessed && len(f.Frames) <= 1 { 264 | defer f.Set(noImagesAvailable) 265 | } 266 | return flag != playing && len(f.Frames) == 0 267 | } 268 | 269 | func (f *ffmpegVideo) HasFrameAvailable() bool { 270 | flag := atomic.LoadInt32(&(f.player.flag)) 271 | 272 | if flag == allImagesProcessed || flag == playing { 273 | return true 274 | } 275 | return false 276 | } 277 | 278 | func (f *ffmpegVideo) WaitUnPause() { 279 | if f.pausedFlag { 280 | <-f.paused 281 | } 282 | } 283 | --------------------------------------------------------------------------------