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

fork from https://github.com/IsaiasSantana/keyboard_utils

example/lib/main.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:keyboard_utils_plugin/keyboard_utils.dart';
import 'package:keyboard_utils_plugin/keyboard_listener.dart' as keyboard_listener;

void main() {
  runApp(const MyApp2());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Keyboard Utils Demo (main2)',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MainPage(),
    );
  }
}

class BarItemModel {
  String label;
  IconData iconData;
  BarItemModel(this.label, this.iconData);
}

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

  @override
  State<MainPage> createState() => MainPageState();
}

class MainPageState extends State<MainPage> {
  int currentIndex = 0;

  List<BarItemModel> get barItems => [
        BarItemModel('input', Icons.keyboard),
        BarItemModel('normal', Icons.chat_bubble_outline),
      ];

  double get bottomSafe => MediaQuery.of(context).padding.bottom;

  @override
  Widget build(BuildContext context) {
    final isToInputPage = currentIndex == 0;
    return Scaffold(
      body: Container(
        alignment: Alignment.center,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              margin: const EdgeInsets.only(bottom: 16),
              child: Text('Current Index: $currentIndex'),
            ),
            Container(
              margin: const EdgeInsets.only(bottom: 24),
              child: Text('Bottom Safe Area: $bottomSafe'),
            ),
            ElevatedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (_) => isToInputPage ? const InputPage() : const NormalPage(),
                  ),
                );
              },
              child: Text('Open: ${isToInputPage ? "Input" : "Normal"} Page'),
            ),
          ],
        ),
      ),
      bottomNavigationBar: BottomNavigationBar(
        selectedItemColor: Colors.blue,
        unselectedItemColor: Colors.grey,
        currentIndex: currentIndex,
        onTap: (index) => setState(() => currentIndex = index),
        items: barItems
            .map((e) => BottomNavigationBarItem(icon: Icon(e.iconData), label: e.label))
            .toList(),
      ),
    );
  }
}

// Minimal bloc demonstrating KeyboardUtils stream usage
class KeyboardBloc {
  final KeyboardUtils _keyboardUtils = KeyboardUtils();
  final StreamController<double> _streamController = StreamController<double>();

  Stream<double> get stream => _streamController.stream;
  KeyboardUtils get keyboardUtils => _keyboardUtils;

  late int _idKeyboardListener;

  void start() {
    _idKeyboardListener = _keyboardUtils.add(
      listener: keyboard_listener.KeyboardListener(
        willHideKeyboard: () {
          _streamController.sink.add(_keyboardUtils.keyboardHeight);
        },
        willShowKeyboard: (double keyboardHeight) {
          _streamController.sink.add(keyboardHeight);
        },
      ),
    );
  }

  void dispose() {
    _keyboardUtils.unsubscribeListener(subscribingId: _idKeyboardListener);
    if (_keyboardUtils.canCallDispose()) {
      _keyboardUtils.dispose();
    }
    _streamController.close();
  }
}

// Input sample using KeyboardUtils to read dynamic keyboard height
class InputPage extends StatefulWidget {
  const InputPage({super.key});

  @override
  State<InputPage> createState() => _InputPageState();
}

class _InputPageState extends State<InputPage> {
  final KeyboardBloc _bloc = KeyboardBloc();
  final KeyboardUtils _keyboardUtils = KeyboardUtils();

  double dynamicKeyboardHeight = 0;
  late int _listenerId;

  double get safeBottom => MediaQuery.of(context).padding.bottom;
  double get safeTop => MediaQuery.of(context).padding.top;
  double get devicePixelRatio => MediaQuery.of(context).devicePixelRatio;

  @override
  void initState() {
    super.initState();
    _listenerId = _keyboardUtils.add(
      listener: keyboard_listener.KeyboardListener(
        willHideKeyboard: () {
          dynamicKeyboardHeight = 0;
          setState(() {});
        },
        willShowKeyboard: (height) {
          dynamicKeyboardHeight = height;
          setState(() {});
        },
      ),
    );
    _bloc.start();
  }

  @override
  void dispose() {
    _bloc.dispose();
    _keyboardUtils.unsubscribeListener(subscribingId: _listenerId);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final viewInsetsBottom = MediaQuery.of(context).viewInsets.bottom;
    final viewPaddingBottom = MediaQuery.of(context).viewPadding.bottom;

    return Scaffold(
      appBar: AppBar(title: const Text('Keyboard Utils - Input')),
      resizeToAvoidBottomInset: false,
      body: Container(
        padding: EdgeInsets.only(bottom: safeBottom),
        child: Center(
          child: Column(
            children: <Widget>[
              Container(
                margin: const EdgeInsets.only(bottom: 16),
                child: Text(
                  'Top safe: $safeTop\n'
                  'Bottom safe: $safeBottom\n'
                  'Bottom inset: $viewPaddingBottom\n'
                  'Is open: ${_keyboardUtils.isKeyboardOpen}\n'
                  'Insets keyboard height: $viewInsetsBottom\n'
                  'Device pixel ratio: $devicePixelRatio\n'
                  'Dynamic keyboard height: $dynamicKeyboardHeight',
                ),
              ),
              const Spacer(),
              Container(
                height: 50,
                padding: const EdgeInsets.symmetric(horizontal: 16),
                child: const TextField(
                  decoration: InputDecoration(hintText: 'Please input'),
                ),
              ),
              // Keep a gap equal to dynamic keyboard height for clarity in this demo
              Container(height: dynamicKeyboardHeight),
            ],
          ),
        ),
      ),
    );
  }
}

// Normal sample reacting to viewInsets changes
class NormalPage extends StatefulWidget {
  const NormalPage({super.key});

  @override
  State<NormalPage> createState() => _NormalPageState();
}

class _NormalPageState extends State<NormalPage> with WidgetsBindingObserver {
  final List<String> message = [];
  final TextEditingController controller = TextEditingController();

  double keyboardHeight = 0;

  double get bottomSafe => MediaQuery.of(context).padding.bottom;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    controller.dispose();
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    final viewInsets = MediaQuery.of(context).viewInsets;
    setState(() {
      keyboardHeight = viewInsets.bottom;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Normal - Bottom Safe: $bottomSafe'),
        centerTitle: true,
      ),
      resizeToAvoidBottomInset: false,
      body: Column(
        children: [
          Expanded(child: _messageView()),
          _inputView(),
          _bottomView(),
        ],
      ),
    );
  }

  Widget _messageView() {
    if (message.isEmpty) {
      return Container(
        alignment: Alignment.center,
        color: Colors.grey.withOpacity(0.2),
        child: const Text('no message'),
      );
    }
    return ListView.builder(
      itemCount: message.length,
      itemBuilder: (context, index) {
        return Container(
          padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
          color: index.isEven ? Colors.grey.withOpacity(0.2) : Colors.green.withOpacity(0.2),
          constraints: const BoxConstraints(minHeight: 44),
          alignment: Alignment.centerLeft,
          child: Text(message[index]),
        );
      },
    );
  }

  Widget _inputView() {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 10),
      color: Colors.red.withOpacity(0.15),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              decoration: const InputDecoration(
                hintText: 'Please input',
                border: InputBorder.none,
                isCollapsed: true,
              ),
              controller: controller,
              maxLines: 4,
              minLines: 1,
              showCursor: true,
            ),
          ),
          TextButton(
            onPressed: () {
              final value = controller.text.trim();
              if (value.isNotEmpty) {
                setState(() => message.add(value));
                controller.clear();
              }
            },
            child: const Text('send'),
          ),
        ],
      ),
    );
  }

  Widget _bottomView() {
    final tempHeight = keyboardHeight;
    return Container(
      padding: EdgeInsets.only(bottom: bottomSafe),
      height: bottomSafe + tempHeight + 1,
      child: const Column(
        children: [
          Divider(height: 1),
          Expanded(
            child: SingleChildScrollView(
              child: SizedBox.shrink(),
            ),
          ),
        ],
      ),
    );
  }
}
1
likes
150
points
299
downloads

Publisher

verified publisherbansook.xyz

Weekly Downloads

fork from https://github.com/IsaiasSantana/keyboard_utils

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on keyboard_utils_plugin

Packages that implement keyboard_utils_plugin