gotify_flutter 1.1.1 copy "gotify_flutter: ^1.1.1" to clipboard
gotify_flutter: ^1.1.1 copied to clipboard

PlatformAndroid

Android Flutter client for Gotify push notifications — WebSocket receive, REST send, local alerts, and background delivery. No Firebase required.

example/lib/main.dart

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

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  GotifyBackgroundService.initCommunicationPort();
  runApp(const GotifyExampleApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'gotify_flutter example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0B6E4F)),
        useMaterial3: true,
      ),
      home: const _RootPage(),
    );
  }
}

class _RootPage extends StatefulWidget {
  const _RootPage();

  @override
  State<_RootPage> createState() => _RootPageState();
}

class _RootPageState extends State<_RootPage> {
  final _gotify = GotifyPushClient();
  bool _checking = true;
  bool _loggedIn = false;

  @override
  void initState() {
    super.initState();
    _restore();
  }

  Future<void> _restore() async {
    final session = await _gotify.restoreSession();
    if (!mounted) return;
    setState(() {
      _loggedIn = session != null;
      _checking = false;
    });
  }

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

  @override
  Widget build(BuildContext context) {
    if (_checking) {
      return const Scaffold(body: Center(child: CircularProgressIndicator()));
    }
    if (!_loggedIn) {
      return LoginPage(
        gotify: _gotify,
        onLoggedIn: () => setState(() => _loggedIn = true),
      );
    }
    return DemoPage(
      gotify: _gotify,
      onLoggedOut: () => setState(() => _loggedIn = false),
    );
  }
}

class LoginPage extends StatefulWidget {
  const LoginPage({super.key, required this.gotify, required this.onLoggedIn});

  final GotifyPushClient gotify;
  final VoidCallback onLoggedIn;

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final _urlController =
      TextEditingController(text: 'https://gotify.example.com');
  final _userController = TextEditingController(text: 'admin');
  final _passController = TextEditingController();
  final _clientController = TextEditingController(text: 'gotify_flutter example');
  bool _busy = false;
  String? _error;

  Future<void> _login() async {
    setState(() {
      _busy = true;
      _error = null;
    });
    try {
      await widget.gotify.login(
        baseUrl: _urlController.text.trim(),
        username: _userController.text.trim(),
        password: _passController.text,
        clientName: _clientController.text.trim().isEmpty
            ? 'gotify_flutter example'
            : _clientController.text.trim(),
      );
      await widget.gotify.ensureSendApplication(name: 'gotify_flutter Example');
      widget.onLoggedIn();
    } catch (e) {
      if (mounted) setState(() => _error = '$e');
    } finally {
      if (mounted) setState(() => _busy = false);
    }
  }

  @override
  void dispose() {
    _urlController.dispose();
    _userController.dispose();
    _passController.dispose();
    _clientController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Connect to Gotify')),
      body: ListView(
        padding: const EdgeInsets.all(24),
        children: [
          TextField(
            controller: _urlController,
            decoration: const InputDecoration(
              labelText: 'Server URL',
              hintText: 'https://gotify.example.com',
              border: OutlineInputBorder(),
            ),
            keyboardType: TextInputType.url,
            autocorrect: false,
          ),
          const SizedBox(height: 12),
          TextField(
            controller: _userController,
            decoration: const InputDecoration(
              labelText: 'Username',
              border: OutlineInputBorder(),
            ),
            autocorrect: false,
          ),
          const SizedBox(height: 12),
          TextField(
            controller: _passController,
            decoration: const InputDecoration(
              labelText: 'Password',
              border: OutlineInputBorder(),
            ),
            obscureText: true,
          ),
          const SizedBox(height: 12),
          TextField(
            controller: _clientController,
            decoration: const InputDecoration(
              labelText: 'Client name',
              border: OutlineInputBorder(),
            ),
          ),
          if (_error != null) ...[
            const SizedBox(height: 16),
            Text(_error!, style: TextStyle(color: Theme.of(context).colorScheme.error)),
          ],
          const SizedBox(height: 24),
          FilledButton(
            onPressed: _busy ? null : _login,
            child: Text(_busy ? 'Connecting…' : 'Connect'),
          ),
        ],
      ),
    );
  }
}

class DemoPage extends StatefulWidget {
  const DemoPage({super.key, required this.gotify, required this.onLoggedOut});

  final GotifyPushClient gotify;
  final VoidCallback onLoggedOut;

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  final _messages = <GotifyMessage>[];
  GotifyStreamState _state = GotifyStreamState.disconnected;
  String? _error;
  bool _ready = false;

  @override
  void initState() {
    super.initState();
    _boot();
  }

  Future<void> _boot() async {
    try {
      widget.gotify.onConnectionState.listen((s) {
        if (mounted) setState(() => _state = s);
      });
      widget.gotify.onMessage.listen((m) {
        if (!mounted) return;
        setState(() => _messages.insert(0, m));
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('${m.displayTitle}: ${m.message}'),
            behavior: SnackBarBehavior.floating,
            action: SnackBarAction(
              label: 'OK',
              onPressed: () {},
            ),
          ),
        );
      });
      // Required for alerts after Home / swipe-away. Android will show a quiet
      // ongoing status row (customizable title/text — cannot be fully removed).
      await widget.gotify.startListening(
        keepAliveInBackground: true,
        keepAliveNotificationTitle: 'gotify_flutter example',
        keepAliveNotificationText: 'Running in background',
      );
      if (mounted) setState(() => _ready = true);
    } catch (e) {
      if (mounted) setState(() => _error = '$e');
    }
  }

  Future<void> _send() async {
    await widget.gotify.sendMessage(
      title: 'Demo',
      message: 'Hello from gotify_flutter example',
      priority: 5,
    );
  }

  Future<void> _logout() async {
    await widget.gotify.logout();
    widget.onLoggedOut();
  }

  @override
  Widget build(BuildContext context) {
    final connected = _state == GotifyStreamState.connected;
    return Scaffold(
      appBar: AppBar(
        title: const Text('gotify_flutter'),
        actions: [
          Padding(
            padding: const EdgeInsets.only(right: 8),
            child: Center(
              child: Text(
                connected ? 'Connected' : _state.name,
                style: TextStyle(color: connected ? Colors.greenAccent : null),
              ),
            ),
          ),
          IconButton(
            tooltip: 'Logout',
            onPressed: _logout,
            icon: const Icon(Icons.logout),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _ready && _error == null ? _send : null,
        icon: const Icon(Icons.send),
        label: const Text('Send demo'),
      ),
      body: _error != null
          ? Center(child: Text(_error!, textAlign: TextAlign.center))
          : !_ready
              ? const Center(child: CircularProgressIndicator())
              : _messages.isEmpty
                  ? const Center(
                      child: Text(
                        'Listening… send a demo notification\n(in-app snackbar + system alert)',
                        textAlign: TextAlign.center,
                      ),
                    )
                  : ListView.separated(
                      itemCount: _messages.length,
                      separatorBuilder: (_, __) => const Divider(height: 1),
                      itemBuilder: (_, i) => ListTile(
                        title: Text(_messages[i].displayTitle),
                        subtitle: Text(_messages[i].message),
                      ),
                    ),
    );
  }
}
0
likes
140
points
40
downloads
screenshot

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Android Flutter client for Gotify push notifications — WebSocket receive, REST send, local alerts, and background delivery. No Firebase required.

Repository (GitHub)
View/report issues

Topics

#gotify #push-notifications #android #websocket #notifications

License

MIT (license)

Dependencies

flutter, flutter_foreground_task, flutter_local_notifications, flutter_secure_storage, http, meta, web_socket_channel

More

Packages that depend on gotify_flutter

Packages that implement gotify_flutter