Flutter焦点管理插件focus_widget的使用

Flutter焦点管理插件focus_widget的使用

让FocusNode失去焦点的Widget

基于FocusNode的可聚焦和可取消焦点的Widget

新增的参数:

  • bool showFocusArea 显示一个半透明的红色方框来展示焦点区域,主要用于调试。

  • void Function(Widget widget, FocusNode focusNode) onLostFocus 失去焦点时会调用这个函数。

当FocusWidget获得焦点后:

  • 在FocusWidget区域外点击
  • 会调用FocusNode.unfocus()并触发FocusNode的监听器

GIF

gif

示例代码

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:focus_widget/focus_widget.dart';
import 'package:oktoast/oktoast.dart';

import 'localizations.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return OKToast(
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        localizationsDelegates: [
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
          GlobalCupertinoLocalizations.delegate,
        ],
        supportedLocales: [
          Locale.fromSubtags(languageCode: 'zh'),
          Locale.fromSubtags(languageCode: 'en'),
        ],
        home: MyHomePage(title: 'Demo'),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  [@override](/user/override)
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final FocusNode myFocusNode = FocusNode();
  GlobalKey<ScaffoldState> _scaffold = GlobalKey();
  final TextEditingController _email = TextEditingController();
  bool checkBoxValue = true;

  Widget leftSideDrawer() {
    return Drawer(
      child: SafeArea(
        child: ListView(
          padding: EdgeInsets.only(left: 10, right: 10),
          children: ListTile.divideTiles(
              context: context,
              tiles: List.generate(20, (i) {
                if (i != 5) {
                  return ListTile(title: Text(i.toString()));
                }
                return FocusWidget.builder(
                  context,
                  showFocusArea: true,
                  builder: (ctx, focusNode) => TextField(
                    focusNode: focusNode,
                    autofocus: true,
                    decoration: InputDecoration(
                      hintText: 'Input',
                      labelText: 'Input',
                    ),
                  ),
                );
              })).toList(),
        ),
      ),
    );
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    final language = DemoLocalizations.of(context);
    return Scaffold(
      key: _scaffold,
      appBar: AppBar(
        title: Text(widget.title),
      ),
      drawer: leftSideDrawer(),
      body: ListView(
        children: ListTile.divideTiles(
          context: context,
          tiles: [
            ListTile(
              title: TextField(
                autofocus: true,
                minLines: 2,
                maxLines: 2,
                controller: TextEditingController(text: language.standardHint),
                decoration: InputDecoration(
                  hintText: language.standardHint,
                  labelText: language.standardLabel,
                ),
              ),
              subtitle: SizedBox(height: 16),
              isThreeLine: true,
            ),
            ListTile(
              title: Text('Interactive widgets'),
              subtitle: Wrap(
                children: [
                  ElevatedButton(
                    child: Text(language.button),
                    onPressed: () {},
                  ),
                  Checkbox(
                    value: checkBoxValue,
                    onChanged: (value) => setState(
                      () {
                        checkBoxValue = value;
                      },
                    ),
                  ),
                ],
              ),
            ),
            ListTile(
              title: FocusWidget(
                focusNode: myFocusNode,
                showFocusArea: false,
                child: TextField(
                  focusNode: myFocusNode,
                  decoration: InputDecoration(
                      hintText: language.normal, labelText: language.normal),
                ),
              ),
            ),
            ListTile(
              title: FocusWidget.builder(
                context,
                showFocusArea: true,
                builder: (_, focusNode) => TextField(
                  focusNode: focusNode,
                  decoration: InputDecoration(
                    hintText: language.showFocusArea,
                    labelText: language.showFocusArea,
                  ),
                ),
              ),
            ),
            ListTile(
              title: FocusWidget.builder(
                context,
                showFocusArea: false,
                onLostFocus: (_, focusNode) async {
                  print('输入为空: ${_email.text.isEmpty}');
                  if (_email.text.isNotEmpty) return;
                  await showDialog(
                      context: context,
                      builder: (_) {
                        return AlertDialog(
                          title: Text(language.alertTitle),
                          content: Text(language.alertContent),
                        );
                      });
                  focusNode.requestFocus();
                },
                builder: (context, FocusNode focusNode) {
                  return TextField(
                    controller: _email,
                    focusNode: focusNode,
                    decoration: InputDecoration(
                      hintText: language.onLostFocus,
                      labelText: language.onLostFocus,
                    ),
                  );
                },
              ),
            ),
            FocusWidget.builder(
              context,
              builder: (_, focusNode) {
                return GestureDetector(
                  onTap: () {
                    focusNode.requestFocus();
                  },
                  child: Focus(
                    focusNode: focusNode,
                    child: ListTile(
                      title: Text(language.forListTileTitle),
                      subtitle: Text(language.forListTileSubtitle),
                    ),
                  ),
                );
              },
              onLostFocus: (_, focusNode) {
                showToast('失去焦点', textPadding: EdgeInsets.all(5));
              },
              showFocusArea: true,
            )
          ],
        ).toList(),
      ),
    );
  }
}

class OtherPage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('空页面'),
      ),
      body: Center(child: Text('空页面')),
    );
  }
}

更多关于Flutter焦点管理插件focus_widget的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter焦点管理插件focus_widget的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用 focus_widget 插件进行 Flutter 焦点管理的代码案例。focus_widget 插件并不是 Flutter 官方提供的插件,而是一个社区插件,它可能提供了一些额外的焦点管理功能。由于具体的 focus_widget 插件可能有所不同,这里假设它提供了一些简化焦点管理的 API。

首先,你需要在 pubspec.yaml 文件中添加该插件的依赖(假设插件名为 focus_widget,并且已经在 pub.dev 上发布):

dependencies:
  flutter:
    sdk: flutter
  focus_widget: ^x.y.z  # 请替换为实际的版本号

然后运行 flutter pub get 来获取依赖。

以下是一个使用 focus_widget 插件管理焦点的示例代码:

import 'package:flutter/material.dart';
import 'package:focus_widget/focus_widget.dart';  // 假设插件提供了这样的导入路径

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: FocusManagementScreen(),
    );
  }
}

class FocusManagementScreen extends StatefulWidget {
  @override
  _FocusManagementScreenState createState() => _FocusManagementScreenState();
}

class _FocusManagementScreenState extends State<FocusManagementScreen> {
  final FocusNode _textFieldFocusNode1 = FocusNode();
  final FocusNode _textFieldFocusNode2 = FocusNode();

  @override
  void dispose() {
    _textFieldFocusNode1.dispose();
    _textFieldFocusNode2.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Focus Management Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            FocusWidget(
              focusNode: _textFieldFocusNode1,
              child: TextField(
                decoration: InputDecoration(labelText: 'TextField 1'),
              ),
            ),
            SizedBox(height: 16),
            FocusWidget(
              focusNode: _textFieldFocusNode2,
              child: TextField(
                decoration: InputDecoration(labelText: 'TextField 2'),
              ),
            ),
            SizedBox(height: 32),
            ElevatedButton(
              onPressed: () {
                // 将焦点从第一个文本字段移动到第二个文本字段
                _textFieldFocusNode1.unfocus();
                FocusScope.of(context).requestFocus(_textFieldFocusNode2);
              },
              child: Text('Move Focus to TextField 2'),
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                // 清除所有焦点
                FocusScope.of(context).unfocus();
              },
              child: Text('Clear Focus'),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们使用了两个 FocusNode 来管理两个 TextField 的焦点。我们还假设 FocusWidget 是插件提供的一个简化焦点管理的组件(请注意,实际的 focus_widget 插件可能提供了不同的 API 或组件)。

  • FocusWidget 组件接收一个 focusNode 并将其应用于其子组件(在这个例子中是 TextField)。
  • 第一个按钮用于将焦点从第一个文本字段移动到第二个文本字段。
  • 第二个按钮用于清除所有焦点。

请注意,由于 focus_widget 插件的具体实现可能有所不同,因此上述代码只是一个假设性的示例。如果 focus_widget 插件提供了不同的 API 或组件,请查阅该插件的文档以获取准确的用法。

回到顶部