nandi/vflutter_ffipublic Fork 0
claude/ssh-key-rickub-25a7d3
Commits
Clone
git clone https://git.rickub.com/nandi/vflutter_ffi.git
git clone ssh://git@rickub.com/nandi/vflutter_ffi.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

main.dart · 121 lines · 3.5 KBDart Blame HistoryRaw
Add Flutter example app with Linux runner 7fdf712 nandi 4h ago1import 'package:flutter/material.dart';
2import 'package:vflutter_ffi/vflutter_ffi.dart' as v;
3
4void main() => runApp(const ExampleApp());
5
6class ExampleApp extends StatelessWidget {
7 const ExampleApp({super.key});
8
9 @override
10 Widget build(BuildContext context) => MaterialApp(
11 title: 'vflutter_ffi',
12 theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
13 home: const HomePage(),
14 );
15}
16
17class HomePage extends StatefulWidget {
18 const HomePage({super.key});
19
20 @override
21 State<HomePage> createState() => _HomePageState();
22}
23
24class _HomePageState extends State<HomePage> {
25 final _controller = TextEditingController(text: 'Flutter');
26
27 // Synchronous results are cheap enough to compute in build(); the async one
28 // is not, so it is held here and refreshed on demand.
29 String _async = '(not run)';
30 String _stress = '(not run)';
31
32 @override
33 void dispose() {
34 _controller.dispose();
35 super.dispose();
36 }
37
38 Future<void> _runAsync() async {
39 final r = await v.greetAsync(_controller.text);
40 setState(() => _async = r);
41 }
42
43 /// 20k round trips on the UI isolate. Fast enough not to jank, and proves
44 /// the V side frees its temporaries under load.
45 void _runStress() {
46 final sw = Stopwatch()..start();
47 for (var i = 0; i < 20000; i++) {
48 v.greet('stress$i');
49 }
50 sw.stop();
51 setState(() => _stress = '20k calls in ${sw.elapsedMilliseconds} ms');
52 }
53
54 @override
55 Widget build(BuildContext context) {
56 return Scaffold(
57 appBar: AppBar(title: const Text('V → Flutter over dart:ffi')),
58 body: Center(
59 child: ConstrainedBox(
60 constraints: const BoxConstraints(maxWidth: 520),
61 child: ListView(
62 padding: const EdgeInsets.all(24),
63 shrinkWrap: true,
64 children: [
65 _Row(label: 'v.add(20, 22)', value: '${v.add(20, 42 - 20)}'),
66 const Divider(height: 32),
67 TextField(
68 controller: _controller,
69 decoration: const InputDecoration(
70 labelText: 'name',
71 border: OutlineInputBorder(),
72 ),
73 onChanged: (_) => setState(() {}),
74 ),
75 const SizedBox(height: 16),
76 _Row(label: 'v.greet(name)', value: v.greet(_controller.text)),
77 const Divider(height: 32),
78 _Row(label: 'v.greetAsync(name)', value: _async),
79 const SizedBox(height: 8),
80 FilledButton.tonal(
81 onPressed: _runAsync,
82 child: const Text('run on background isolate'),
83 ),
84 const Divider(height: 32),
85 _Row(label: 'ownership stress', value: _stress),
86 const SizedBox(height: 8),
87 FilledButton.tonal(
88 onPressed: _runStress,
89 child: const Text('run 20k round trips'),
90 ),
91 ],
92 ),
93 ),
94 ),
95 );
96 }
97}
98
99class _Row extends StatelessWidget {
100 const _Row({required this.label, required this.value});
101
102 final String label;
103 final String value;
104
105 @override
106 Widget build(BuildContext context) {
107 final theme = Theme.of(context);
108 return Column(
109 crossAxisAlignment: CrossAxisAlignment.start,
110 children: [
111 Text(label, style: theme.textTheme.labelMedium),
112 const SizedBox(height: 4),
113 SelectableText(
114 value,
115 style: theme.textTheme.titleMedium
116 ?.copyWith(fontFamily: 'monospace', color: theme.colorScheme.primary),
117 ),
118 ],
119 );
120 }
121}