preface

Tinypng is a popular image compression site in the design and technology world, but it is web only, with no GUI. Fortunately, it supports compression of images directly through the API Key. Although there are many versions of Flutter, Windows, and Mac in the industry, the recent learning of Flutter Deskstop can be used for real use. The current version is only packaged with the MacOS version, Windows version I have time to find a machine to debug, theoretically not much change.

code

Implement selection file

I choose the implementation of file. As I am a developer of iOS, the native development of macOS is almost the same. However, in order to be compatible with multiple terminals, I don’t bother to write plug-ins one by one. Linux supports it all. At present, only JPG and PNG files are supported. It seems that webP and H265 are supported. I can add them later.

void _pickFiles() async { if (await controller.checkHaveApiKey() == false) { _showSettingBottomSheet(); showToast("Please enter your TinyPNG Apikey", textPadding: EdgeInsets.all(15)); return; } FilePickerResult? result = await FilePicker.platform.pickFiles(allowMultiple: true); if (result ! = null) { List<File> files = result.paths.map((path) => File(path ?? "")).toList(); List<File> chooseFiles = []; files.forEach((element) { if (element.path.toLowerCase().endsWith("jpg") || element.path.toLowerCase().endsWith("jpeg") || element.path.toLowerCase().endsWith("png")) { chooseFiles.add(element); } else { showToast('invalid image file', textPadding: EdgeInsets.all(15)); print("invalid image file : ${element.path}"); }}); if (chooseFiles.isNotEmpty) { controller.refreshWithFileList(chooseFiles); } } else { showToast("Cancel Pick files", textPadding: EdgeInsets.all(15)); }}Copy the code

Realize open directory or open url

At the beginning, I thought for a long time that the iOS terminal should jump through urlLaunch. After searching the PUB, many on-site libraries only support iOS and Android, which are not very friendly to the desktop. The Swift script can run the terminal command directly through the Process class. Does DART have relevant API support? To open the directory, run the open XXX command. To open the url, run the open xxx.com command. The answer is obvious. Dart also encapsulates the Proccess class, as shown below.

// Open the compressed image directory process. run("open", [savePath]); / / open jump site Process. The run (" open, "/" https://tinypng.com/developers ");Copy the code

Implement upload original image file to Tiny

This is nothing to say, just look at the HTTP rules, just pull out the code.

Future<TinyImageInfo? > uploadOriginImage({required Uint8List? buffer}) async { SharedPreferences prefs = await SharedPreferences.getInstance(); var apiKey = prefs.getString(KApiKey); if (apiKey == null || apiKey.length == 0) { return null; } var url = "api.tinify.com"; Uri uri = Uri.https(url, "/shrink"); var auth = "api:$apiKey"; var authData = base64Encode(utf8.encode(auth)); var authorizationHeader = "Basic " + authData; var headers = { "Accept": "application/json", "Authorization": authorizationHeader, }; try { var response = await http.post(uri, headers: headers, body: buffer); if (response.statusCode ! = 201) { print("fail code is ${response.statusCode}"); return null; } else { var json = jsonDecode(utf8.decode(response.bodyBytes)); var jsonString = jsonEncode(json); print("success json $jsonString"); return TinyImageInfo.fromJson(json); } } catch (e) { print("catch upload error $e"); return null; }}Copy the code

Download compressed images to their own directory

TinyPng uploads the image successfully and returns a string of Json after the compression process is complete

{“input”:{“size”:84736,”type”:”image/webp”},”output”:{“size”:68282,”type”:”image/webp”,”width”:658,”height”:1009,”ratio” : 0.8058, “url” : “api.tinify.com/output/avxq…

It contains compression ratio, original image size, compressed size, compressed output address and so on. With this JSON we can naturally build our UI.


Future<bool> downloadOutputImage(TinyImageInfo imageInfo, String savePath,

{Function(int count, int total)? onReceiveProgress}) async {

String? url = imageInfo.output?.url;

String? type = imageInfo.output?.type;

if (url == null || type == null) {

return false;

}

Uri uri = Uri.parse(url);

var dio = Dio();

try {

var rsp = await dio.downloadUri(

uri,

savePath,

options: Options(

headers: {"Accept": type, "Content-Type": "application/json"},

),

onReceiveProgress: (count, total) {

onReceiveProgress?.call(count, total);

},

);

return rsp.statusCode == 200;

} catch (e) {

return false;

}

}

Copy the code

The Mac application permission is incorrect

You need to configure these permissions; otherwise, the application will report various permissions errors.


<key>com.apple.security.app-sandbox</key>

<false/>

<key>com.apple.security.cs.allow-jit</key>

<true/>

<key>com.apple.security.network.server</key>

<true/>

<key>com.apple.security.network.client</key>

<true/>

<key>com.apple.security.files.user-selected.read-write</key>

Copy the code

State management

I used the current popular Gex state management, which only listens for a few properties.

final PathProviderPlatform provider = PathProviderUtil.provider(); var taskList = <TinyImageInfoItemViewModel>[].obs; var savePath = "".obs; var apiKey = "".obs; var taskCount = 0.obs; Var saveKb = 0.0. Obs.Copy the code

Tripartite library used

  • http

  • dio

  • file_picker

  • path_provider

  • path_provider_macos

  • get

  • oktoast

  • shared_preferences

The project address

This project is completely open source. If you want to learn it, you can go to GitHub to check it. If you have any trouble, please give a Star. This project is based on Flutter 2.2.3, which is theoretically compatible with a higher version. (No actual measurement)

Github.com/JerryFans/T…

The installation package

If you do not want to compile, you can use the installation package directly

DMG installation package

PKG installer

In the future

1. Package the Windows version

2. Linux Version? (Not many people seem to use it)

3, MAC version support file drag past (see MAC AppKit documentation, this is not very difficult, just need to make a plug-in, the future will be done and open source)

4. Make the AppStore available to muggles