flutter_minizip_ffi_bindings 0.1.0+2 flutter_minizip_ffi_bindings: ^0.1.0+2 copied to clipboard
FFI bindings for [nmoinvaz/minizip](https://github.com/nmoinvaz/minizip) made using [dart-lang/ffigen](https://github.com/dart-lang/ffigen) and trivial trick.
import 'dart:convert';
import 'dart:ffi' as ffi;
import 'dart:io';
import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_minizip_ffi_bindings/flutter_minizip_ffi_bindings.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
const _sampleDataName = 'minizip-master.zip';
Future<void> _copySampleData() async {
final sampleData = await rootBundle.load('assets/$_sampleDataName');
final filesDir = await getApplicationSupportDirectory();
final dstPath = p.join(filesDir.path, _sampleDataName);
final dst = File(dstPath);
await dst.writeAsBytes(sampleData.buffer.asUint8List());
}
final _minizip = () {
final lib = Platform.isIOS
? ffi.DynamicLibrary.process()
: ffi.DynamicLibrary.open('libminizip.so');
return Minizip(lib);
}();
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await _copySampleData();
runApp(const _App());
}
@immutable
class _App extends StatelessWidget {
const _App({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('flutter_minizip_ffi_bindings_example'),
),
body: const _Body(),
),
);
}
}
@immutable
class _Body extends StatefulWidget {
const _Body({
Key key,
}) : super(key: key);
@override
_BodyState createState() => _BodyState();
}
class _BodyState extends State<_Body> {
List<String> _paths;
Future<void> _listFilesInZip() async {
final filesDir = await getApplicationSupportDirectory();
final path = p.join(filesDir.path, _sampleDataName);
final handle = allocate<ffi.Pointer<ffi.Void>>();
_minizip.mz_zip_reader_create(handle);
_minizip.mz_zip_reader_open_file(handle.value, Utf8.toUtf8(path).cast());
final paths = <String>[];
_minizip.mz_zip_reader_goto_first_entry(handle.value);
do {
if (_minizip.mz_zip_reader_entry_is_dir(handle.value) == 0) {
continue;
}
final info = allocate<ffi.Pointer<mz_zip_file_s>>();
_minizip.mz_zip_reader_entry_get_info(handle.value, info);
paths.add(Utf8.fromUtf8(info.value.ref.filename.cast()));
} while (_minizip.mz_zip_reader_goto_next_entry(handle.value) == 0);
_minizip.mz_zip_reader_close(handle.value);
_minizip.mz_zip_reader_delete(handle);
setState(() => _paths = paths);
}
@override
void initState() {
super.initState();
_listFilesInZip();
}
@override
Widget build(BuildContext context) {
if (_paths == null) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Scrollbar(
child: ListView.builder(
itemCount: _paths.length,
itemBuilder: (context, index) {
return _ListTile(path: _paths[index]);
},
),
);
}
}
@immutable
class _ListTile extends StatelessWidget {
const _ListTile({
@required this.path,
Key key,
}) : super(key: key);
final String path;
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).dividerColor,
),
),
),
child: ListTile(
title: Text(path),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return _Detail(path: path);
},
),
);
},
),
);
}
}
@immutable
class _Detail extends StatelessWidget {
const _Detail({
@required this.path,
Key key,
}) : super(key: key);
final String path;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(path),
),
body: _Content(path: path),
);
}
}
@immutable
class _Content extends StatelessWidget {
const _Content({
@required this.path,
Key key,
}) : super(key: key);
final String path;
Future<List<String>> _readFileInZip(String filePath) async {
final filesDir = await getApplicationSupportDirectory();
final path = p.join(filesDir.path, _sampleDataName);
final handle = allocate<ffi.Pointer<ffi.Void>>();
_minizip.mz_zip_reader_create(handle);
_minizip.mz_zip_reader_open_file(handle.value, Utf8.toUtf8(path).cast());
_minizip.mz_zip_reader_locate_entry(
handle.value,
Utf8.toUtf8(filePath).cast(),
0,
);
final info = allocate<ffi.Pointer<mz_zip_file_s>>();
_minizip.mz_zip_reader_entry_get_info(handle.value, info);
final len = info.value.ref.uncompressed_size;
final buf = allocate<ffi.Uint8>(count: len);
_minizip.mz_zip_reader_entry_open(handle.value);
_minizip.mz_zip_reader_entry_read(handle.value, buf.cast(), len);
final bytes = List<int>.from(buf.asTypedList(len));
_minizip.mz_zip_reader_entry_close(handle.value);
_minizip.mz_zip_reader_close(handle.value);
_minizip.mz_zip_reader_delete(handle);
final lines = () {
try {
return const Utf8Decoder().convert(bytes);
} catch (e) {
return 'Error: Invalid UTF-8 Strings';
}
}();
return const LineSplitter().convert(lines);
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<String>>(
future: _readFileInZip(path),
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Center(
child: CircularProgressIndicator(),
);
}
final lines = snapshot.data;
return Scrollbar(
child: SafeArea(
child: ListView.builder(
itemCount: lines.length,
itemBuilder: (context, index) {
return Text(lines[index]);
},
),
),
);
},
);
}
}