cross_tab 0.1.2 copy "cross_tab: ^0.1.2" to clipboard
cross_tab: ^0.1.2 copied to clipboard

Coordinate the browser tabs of a Flutter Web app: send typed messages between them, know how many are open, and elect exactly one to do the work.

example/lib/main.dart

import 'dart:async';

import 'package:cross_tab/cross_tab.dart';
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) => MaterialApp(
    title: 'cross_tab',
    debugShowCheckedModeBanner: false,
    theme: ThemeData(
      colorSchemeSeed: const Color(0xFF4C6FFF),
      useMaterial3: true,
    ),
    home: const TabsPage(),
  );
}

/// Open this page in two or three browser tabs to see it work.
class TabsPage extends StatefulWidget {
  const TabsPage({super.key});

  @override
  State<TabsPage> createState() => _TabsPageState();
}

class _TabsPageState extends State<TabsPage> {
  final CrossTab _tabs = CrossTab.open('cross-tab-demo');
  final List<String> _log = <String>[];
  final List<StreamSubscription<Object?>> _subs =
      <StreamSubscription<Object?>>[];
  TabPresence? _presence;
  Timer? _leaderWork;

  @override
  void initState() {
    super.initState();
    _subs.add(
      _tabs.messages.listen((TabMessage m) {
        setState(() => _log.insert(0, '${m.from.substring(0, 4)} → ${m.data}'));
      }),
    );
    _subs.add(
      _tabs.presence.listen((TabPresence p) {
        setState(() => _presence = p);
        // The point of a leader: exactly one tab does this, and if that tab
        // closes another picks it up without anything being told to.
        if (p.isLeader) {
          _leaderWork ??= Timer.periodic(const Duration(seconds: 3), (_) {
            setState(
              () => _log.insert(0, 'leader tick (only one tab does this)'),
            );
          });
        } else {
          _leaderWork?.cancel();
          _leaderWork = null;
        }
      }),
    );
  }

  @override
  void dispose() {
    _leaderWork?.cancel();
    for (final StreamSubscription<Object?> s in _subs) {
      s.cancel();
    }
    _tabs.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final TabPresence? p = _presence;
    return Scaffold(
      appBar: AppBar(
        title: const Text('cross_tab'),
        actions: <Widget>[
          if (p != null)
            Padding(
              padding: const EdgeInsets.only(right: 12),
              child: Chip(
                label: Text(p.isLeader ? 'leader' : 'follower'),
                backgroundColor: p.isLeader
                    ? Colors.green.shade100
                    : Colors.grey.shade200,
              ),
            ),
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            if (!CrossTab.isSupported)
              const Text(
                'Not the web — this is a single instance, always the '
                'leader. Which is the correct answer, not a fallback.',
              ),
            Text(
              p == null
                  ? 'starting…'
                  : '${p.count} tab(s) open · this one is ${p.me.substring(0, 4)}'
                        ' · leader ${p.leader?.substring(0, 4) ?? "electing"}',
              style: Theme.of(context).textTheme.titleSmall,
            ),
            const SizedBox(height: 4),
            Text(
              'Open this page in another tab to watch it join.',
              style: Theme.of(context).textTheme.labelMedium,
            ),
            const SizedBox(height: 12),
            Row(
              children: <Widget>[
                FilledButton(
                  onPressed: () => _tabs.send(<String, Object?>{
                    'hello': 'from ${_tabs.id.substring(0, 4)}',
                    'at': DateTime.now().toIso8601String().substring(11, 19),
                  }),
                  child: const Text('Send to the other tabs'),
                ),
              ],
            ),
            const Divider(height: 24),
            Expanded(
              child: _log.isEmpty
                  ? const Center(child: Text('nothing heard yet'))
                  : ListView.builder(
                      itemCount: _log.length,
                      itemBuilder: (BuildContext c, int i) =>
                          ListTile(dense: true, title: Text(_log[i])),
                    ),
            ),
          ],
        ),
      ),
    );
  }
}
0
likes
160
points
--
downloads

Documentation

Documentation
API reference

Publisher

verified publisherjumyn.com

Coordinate the browser tabs of a Flutter Web app: send typed messages between them, know how many are open, and elect exactly one to do the work.

Repository (GitHub)
View/report issues

Topics

#web #broadcastchannel #tabs #leader-election #browser

License

MIT (license)

Dependencies

flutter, web

More

Packages that depend on cross_tab