nandi/vflutter_ffipublic Fork 0
7fdf71219e3dd44c76c7be4120ff73a31d6237ce
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.

Add Flutter example app with Linux runner 7fdf712 · on 7fdf71219e3dd44c76c7be4120ff73a31d6237ce · nandi · 7h ago
main.dart · 121 lines · 3.5 KBDart Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import 'package:flutter/material.dart';
import 'package:vflutter_ffi/vflutter_ffi.dart' as v;

void main() => runApp(const ExampleApp());

class ExampleApp extends StatelessWidget {
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) => MaterialApp(
        title: 'vflutter_ffi',
        theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
        home: const HomePage(),
      );
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final _controller = TextEditingController(text: 'Flutter');

  // Synchronous results are cheap enough to compute in build(); the async one
  // is not, so it is held here and refreshed on demand.
  String _async = '(not run)';
  String _stress = '(not run)';

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  Future<void> _runAsync() async {
    final r = await v.greetAsync(_controller.text);
    setState(() => _async = r);
  }

  /// 20k round trips on the UI isolate. Fast enough not to jank, and proves
  /// the V side frees its temporaries under load.
  void _runStress() {
    final sw = Stopwatch()..start();
    for (var i = 0; i < 20000; i++) {
      v.greet('stress$i');
    }
    sw.stop();
    setState(() => _stress = '20k calls in ${sw.elapsedMilliseconds} ms');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('V → Flutter over dart:ffi')),
      body: Center(
        child: ConstrainedBox(
          constraints: const BoxConstraints(maxWidth: 520),
          child: ListView(
            padding: const EdgeInsets.all(24),
            shrinkWrap: true,
            children: [
              _Row(label: 'v.add(20, 22)', value: '${v.add(20, 42 - 20)}'),
              const Divider(height: 32),
              TextField(
                controller: _controller,
                decoration: const InputDecoration(
                  labelText: 'name',
                  border: OutlineInputBorder(),
                ),
                onChanged: (_) => setState(() {}),
              ),
              const SizedBox(height: 16),
              _Row(label: 'v.greet(name)', value: v.greet(_controller.text)),
              const Divider(height: 32),
              _Row(label: 'v.greetAsync(name)', value: _async),
              const SizedBox(height: 8),
              FilledButton.tonal(
                onPressed: _runAsync,
                child: const Text('run on background isolate'),
              ),
              const Divider(height: 32),
              _Row(label: 'ownership stress', value: _stress),
              const SizedBox(height: 8),
              FilledButton.tonal(
                onPressed: _runStress,
                child: const Text('run 20k round trips'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _Row extends StatelessWidget {
  const _Row({required this.label, required this.value});

  final String label;
  final String value;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(label, style: theme.textTheme.labelMedium),
        const SizedBox(height: 4),
        SelectableText(
          value,
          style: theme.textTheme.titleMedium
              ?.copyWith(fontFamily: 'monospace', color: theme.colorScheme.primary),
        ),
      ],
    );
  }
}