启用/禁用按钮当底部表单内有输入时。

huangapple go评论69阅读模式
英文:

Enable/Disable button when TextField has input inside a BottomSheet

问题

我要在BottomSheet内有一个简单的TextField和一个TextButton。只有在TextField中有文本时,TextButton才应该处于启用状态。然而,它只在我点击屏幕或按下回车键时启用/禁用按钮。我希望它能实时更改。

以下是我的完整代码:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyBottomSheet(),
    );
  }
}

class MyBottomSheet extends StatefulWidget {
  @override
  _MyBottomSheetState createState() => _MyBottomSheetState();
}

class _MyBottomSheetState extends State<MyBottomSheet> {
  late TextEditingController _textController;
  bool _isButtonDisabled = true;

  @override
  void initState() {
    super.initState();
    _textController = TextEditingController();
    _textController.addListener(_onTextChanged);
  }

  void _onTextChanged() {
    setState(() {
      _isButtonDisabled = _textController.text.isEmpty;
    });
  }

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

  void _showBottomSheet() {
    showModalBottomSheet(
      context: context,
      builder: (BuildContext context) {
        return Padding(
          padding: EdgeInsets.only(
            bottom: MediaQuery.of(context).viewInsets.bottom,
          ),
          child: Container(
            height: 300,
            width: double.infinity,
            child: Column(
              children: [
                TextField(
                  controller: _textController,
                  autofocus: true,
                  decoration: const InputDecoration(
                    contentPadding: EdgeInsets.symmetric(horizontal: 15),
                    hintText: '输入姓名...',
                  ),
                ),
                TextButton(
                  onPressed: _isButtonDisabled ? null : () {
                    // 在这里处理按钮点击事件
                  },
                  child: const Text('保存'),
                )
              ],
            ),
          ),
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('测试'),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      floatingActionButton: FloatingActionButton(
        shape: CircleBorder(),
        onPressed: _showBottomSheet,
        child: Icon(Icons.add_rounded),
      ),
      bottomNavigationBar: BottomAppBar(
        height: 60,
        shape: CircularNotchedRectangle(),
        notchMargin: 8.0,
        clipBehavior: Clip.antiAlias,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            IconButton(
              onPressed: () {},
              icon: Icon(Icons.more_horiz_rounded),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(Icons.menu_rounded),
            )
          ],
        ),
      ),
    );
  }
}

我在BottomSheet之外尝试了相同的方法,它可以正常工作。

英文:

I want to have a simple TextField and a TextButton inside a BottomSheet. The TextButton should only be enabled if there is some text in the TextField. However it only enables/disables the button when I click the screen or enter. I want it to change in real time.

Here is my full code:

import &#39;package:flutter/material.dart&#39;;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: &#39;Flutter Demo&#39;,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyBottomSheet(),
);
}
}
class MyBottomSheet extends StatefulWidget {
@override
_MyBottomSheetState createState() =&gt; _MyBottomSheetState();
}
class _MyBottomSheetState extends State&lt;MyBottomSheet&gt; {
late TextEditingController _textController;
bool _isButtonDisabled = true;
@override
void initState() {
super.initState();
_textController = TextEditingController();
_textController.addListener(_onTextChanged);
}
void _onTextChanged() {
setState(() {
_isButtonDisabled = _textController.text.isEmpty;
});
}
@override
void dispose() {
_textController.dispose();
super.dispose();
}
void _showBottomSheet() {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom
),
child: Container(
height: 300,
width: double.infinity,
child: Column(
children: [
TextField(
controller: _textController,
autofocus: true,
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 15),
hintText: &#39;Enter name...&#39;,
),
),
TextButton(
onPressed: _isButtonDisabled ? null : () {
},
child: const Text(&#39;Save&#39;),
)
],
),
),
);
}
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(&#39;TEST&#39;),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked ,
floatingActionButton: FloatingActionButton(
shape: CircleBorder(),
onPressed: (_showBottomSheet),
child: Icon(Icons.add_rounded),
),
bottomNavigationBar: BottomAppBar(
height: 60,
shape: CircularNotchedRectangle(),
notchMargin: 8.0,
clipBehavior: Clip.antiAlias,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: () {},
icon: Icon(Icons.more_horiz_rounded),
),
IconButton(
onPressed: () {},
icon: Icon(Icons.menu_rounded)
)
],
),
),
);
}
void _onSubmit() {
// Handle form submission here
}
}

I tried the same approach outside the BottomSheet and it works fine.

答案1

得分: 0

以下是您要翻译的内容:

你必须将模态内容移到一个单独的StatefulWidget,如下所示

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyBottomSheet(),
    );
  }
}
class MyBottomSheet extends StatefulWidget {
  @override
  State<MyBottomSheet> createState() => _MyBottomSheetState();
}

class _MyBottomSheetState extends State<MyBottomSheet> {
  
  late TextEditingController _textController;
  
  @override
  void initState() {
    super.initState();
    _textController = TextEditingController();
  }
  
  @override
  void dispose() {
    _textController.dispose();
    super.dispose();
  }
    
  void _showBottomSheet() {
    showModalBottomSheet(
      context: context,
      builder: (BuildContext context) {
        return ModalContent(controller: _textController);
      }
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('TEST'),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked ,
        floatingActionButton: FloatingActionButton(
          shape: const CircleBorder(),
          onPressed: (_showBottomSheet),
          child: const Icon(Icons.add_rounded),
        ),
        bottomNavigationBar: BottomAppBar(
          height: 60,
          shape: const CircularNotchedRectangle(),
          notchMargin: 8.0,
          clipBehavior: Clip.antiAlias,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.more_horiz_rounded),
              ),
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.menu_rounded)
              )
            ],
          ),
        ),
    );
  }

  void _onSubmit() {
    // 处理表单提交
  }
}

class ModalContent extends StatefulWidget {
  final TextEditingController controller;
  const ModalContent({super.key, required this.controller});
  
  @override
  State<ModalContent> createState() => _ModalContentState();
}

class _ModalContentState extends State<ModalContent> {
  bool _isButtonDisabled = true;
  
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(
        bottom: MediaQuery.of(context).viewInsets.bottom
      ),
      child: Container(
        height: 300,
        width: double.infinity,
        child: Column(
          children: [
            TextField(
              controller: widget.controller,
              autofocus: true,
              decoration: const InputDecoration(
                contentPadding: EdgeInsets.symmetric(horizontal: 15),
                hintText: '输入姓名...',
              ),
              onChanged: (value) {
                print("值为空 : ${value.isEmpty}");
                setState(() {
                  _isButtonDisabled = value.isEmpty;
                });
              }
            ),
            TextButton(
              onPressed: _isButtonDisabled ? null : () {

              },
              child: const Text('保存'),
            )
          ],
        ),
      ),
    );
  }
}

<details>
<summary>英文:</summary>
You must move Modal content into a separated StatefulWidget like bellow
```dart
import &#39;package:flutter/material.dart&#39;;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: &#39;Flutter Demo&#39;,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyBottomSheet(),
);
}
}
class MyBottomSheet extends StatefulWidget {
@override
State&lt;MyBottomSheet&gt; createState() =&gt; _MyBottomSheetState();
}
class _MyBottomSheetState extends State&lt;MyBottomSheet&gt; {
late TextEditingController _textController;
@override
void initState() {
super.initState();
_textController = TextEditingController();
}
@override
void dispose() {
_textController.dispose();
super.dispose();
}
void _showBottomSheet() {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return ModalContent(controller: _textController);
}
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(&#39;TEST&#39;),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked ,
floatingActionButton: FloatingActionButton(
shape: const CircleBorder(),
onPressed: (_showBottomSheet),
child: const Icon(Icons.add_rounded),
),
bottomNavigationBar: BottomAppBar(
height: 60,
shape: const CircularNotchedRectangle(),
notchMargin: 8.0,
clipBehavior: Clip.antiAlias,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.more_horiz_rounded),
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.menu_rounded)
)
],
),
),
);
}
void _onSubmit() {
// Handle form submission here
}
}
class ModalContent extends StatefulWidget {
final TextEditingController controller;
const ModalContent({super.key, required this.controller});
@override
State&lt;ModalContent&gt; createState() =&gt; _ModalContentState();
}
class _ModalContentState extends State&lt;ModalContent&gt; {
bool _isButtonDisabled = true;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom
),
child: Container(
height: 300,
width: double.infinity,
child: Column(
children: [
TextField(
controller: widget.controller,
autofocus: true,
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 15),
hintText: &#39;Enter name...&#39;,
),
onChanged: (value) {
print(&quot;Value is empty : ${value.isEmpty}&quot;);
setState(() {
_isButtonDisabled = value.isEmpty;
});
}
),
TextButton(
onPressed: _isButtonDisabled ? null : () {
},
child: const Text(&#39;Save&#39;),
)
],
),
),
);
}
}

huangapple
  • 本文由 发表于 2023年4月10日 19:48:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75976831.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定