css 颤动_颤动介质,如文本编辑器

css 颤动

颤振教程 (Flutter Tutorial)

In the following tutorial, we‘re going to build a simplified version of the text editor used in the ‘New Story’ section of the Medium mobile app. Afterward, your app should look like this:

在下面的教程中,我们将构建中型移动应用程序的“新故事”部分中使用的文本编辑器的简化版本。 之后,您的应用应如下所示:

Image for post
The final result in action
最终结果在行动

对中型应用程序中“新故事”部分的简短分析 (A short analysis of the ‘New Story’ Section in the Medium App)

If you navigate to the ‘New story’ section in the Medium app, you’ll notice that the keyboard toolbar is apparent at the bottom of the screen even if the keyboard is not visible. When the keyboard appears, the toolbar moves up to display itself above the keyboard. If you create two text blocks, you’ll see that trying to select text from multiple blocks will fail. You can only do so for one text block at a time. Therefore, we can see that Medium probably uses a ListView of TextFields with different stylings.

如果您导航到“中型”应用程序中的“新故事”部分,则您会注意到,即使键盘不可见,键盘工具栏也会显示在屏幕底部。 出现键盘时,工具栏将向上移动以在键盘上方显示自己。 如果创建两个文本块,您将看到尝试从多个块中选择文本将失败。 您一次只能对一个文本块执行此操作。 因此,我们可以看到Medium可能使用具有不同样式的TextFieldsListView

开始之前 (Before Getting Started)

First, lets’s create a new Flutter project

首先,让我们创建一个新的Flutter项目

flutter create medium_text_editor

Now, we add the following packages to the pubspec.yaml inside the dependencies:

现在,我们将以下包添加到dependencies: pubspec.yaml内的pubspec.yaml dependencies:

provider: ^4.1.3
community_material_icon: ^5.3.45
keyboard_visibility: ^0.5.6

and install the packages with

并安装软件包

flutter packages get

第1部分:使用键盘工具栏设置文本格式 (Part 1: Text Formatting using a Keyboard Toolbar)

Keep it simple and focus on what matters.Don’t let yourself be overwhelmed.- Confucius -

保持简单,专注于重要事情。不要让自己不知所措。-Kong子-

In the first part, we will keep the implementation as simple as possible. We will focus on displaying a keyboard toolbar that changes the text formatting of a text field on button clicks.

在第一部分中,我们将使实现尽可能简单。 我们将重点显示键盘工具栏,该工具栏可在单击按钮时更改文本字段的文本格式。

文字格式 (Text Formatting)

We will only implement headings, plain text, bullet points, and quotes as text formatting options. Let’s specify the text style and padding for each option.

我们仅将标题,纯文本,项目符号和引号用作文本格式设置选项。 让我们为每个选项指定文本样式和填充。

text_field.dart
text_field.dart

Note: We used the Unicode character \u2002 as a prefix for the bullet points.

注意: 我们使用Unicode字符 \u2002 作为项目符号的前缀。

可自定义的键盘工具栏 (Customizable Keyboard Toolbar)

Next, we will build the keyboard toolbar that we will display above the keyboard. The Toolbar is just a Row of IconButtons that set the selected formatting type on a click.

接下来,我们将构建键盘工具栏,该工具栏将显示在键盘上方。 工具栏只是一Row IconButtons ,可在单击时设置选定的格式类型。

Note: We won’t use the keyboard_actions package as it is not very flexible and leads to lots of weird implementations.

注意: 我们不会使用 keyboard_actions 包,因为它不是很灵活,并且会导致很多奇怪的实现。

toolbar.dart
工具栏

连接键盘工具栏和文本格式 (Connecting the Keyboard Toolbar and Text Formatting)

For now, state management is quite easy and only requires to update the selected text type. Since it will become more complicated in the second part, we’ll use the Provider framework for simplicity.

目前,状态管理非常简单,只需要更新所选的文本类型即可。 由于第二部分将变得更加复杂,因此我们将使用Provider框架来简化操作。

Note: The EditorProvider notifier will become more useful later on.

EditorProvider 通知将变得更加有用以后。

state_management.dart
state_management.dart

Now that we defined our ChangeNotifier , we need to initialize our state using theChangeNotifierProvider.

现在我们定义了ChangeNotifier ,我们需要使用ChangeNotifierProvider.初始化状态ChangeNotifierProvider.

To display the toolbar above the keyboard, we simply use Stack and position our SmartTextField with bottom: 56 and our Toolbar with bottom: 0. The resizeToAvoidBottomInset property of Scaffold will automatically adjust the layout if a keyboard is visible so that the toolbar is displayed above.

要在键盘上方显示工具栏,我们只需使用Stack并将SmartTextFieldbottom: 56Toolbarbottom: 0. resizeToAvoidBottomInset 。如果可见键盘,则ScaffoldresizeToAvoidBottomInset属性将自动调整布局,以便工具栏显示在上方。

text_editor.dart
text_editor.dart

风格很重要 (Style Matters)

Since it’s always more pleasant to code if things don’t look like shit, we modify the theme in the main.dart.

因为如果事情看起来不怎么样,编码总是更令人愉快,所以我们在main.dart修改了主题。

main.dart
main.dart

Ta-da🎉

塔达🎉

Image for post

第2部分:从单行到全文编辑器 (Part 2: Going from Single-Line to a Full-Text Editor)

Now that we implemented the single TextField case, it is time to build a fully functional text editor.

现在,我们实现了单个TextField情况,是时候构建一个功能齐全的文本编辑器了。

丰富国家管理 (Enriching the State Management)

As mentioned before, we need a ListView of TextFields . Therefore, we will store three lists in the EditorProvider :

如前所述,我们需要一个TextFieldsListView 。 因此,我们将三个列表存储在EditorProvider

  • List<SmartTextType> contains the formatting style for each block

    List<SmartTextType>包含每个块的格式样式

  • List<TextEditingController> contains the controllers for each block that we can use to listen to user input

    List<TextEditingController>包含每个块的控制器,我们可以使用它们来监听用户输入

  • List<FocusNode> contains a FocusNode for each block that we will use to manage the focus of the TextFields

    List<FocusNode>包含每个块的FocusNode ,我们将使用它们管理TextFields的焦点

For simplicity and readability, we create the following five getters that we will use at multiple parts in the code:

为了简单起见,我们创建了以下五个getter,将在代码的多个部分使用它们:

  • length: returns the number of text blocks

    length:返回文本块的数量

  • focus: returns the index of the block that has the current focus

    focus:返回具有当前焦点的块的索引

  • nodeAt: returns the FocusNode at a certain index

    nodeAt:在特定索引处返回FocusNode

  • textAt: returns the TextEditingController at a certain index

    textAt:在特定索引处返回TextEditingController

  • typeAt: returns the SmartTextType at a certain index

    typeAt:在特定索引处返回SmartTextType

Additionally, we will add a setFocus() method to update the keyboard toolbar when the user changes the focus to another text block and an insert() method to add a new text block.

另外,当用户将焦点更改到另一个文本块时,我们将添加setFocus()方法来更新键盘工具栏,并添加insert()方法来添加新的文本块。

Ok, let’s update our EditorProvider using the following implementation:

好的,让我们使用以下实现更新我们的EditorProvider

state_management.dart
state_management.dart

Now that we changed the EditorProvider to store a list of text blocks, we need to update our TextEditor to display a list of SmartTextFields . To do so, go to the implementation of the TextEditor , and at line 31 replace the Consumer with the following code.

现在,我们更改了EditorProvider来存储文本块列表,我们需要更新TextEditor以显示SmartTextFields列表。 为此,转到TextEditor的实现,并在第31行用以下代码替换Consumer

Consumer<EditorProvider>(
builder: (context, state, _) {
return ListView.builder(
itemCount: state.length,
itemBuilder: (context, index) {
return Focus(
onFocusChange: (hasFocus) {
if (hasFocus) state.setFocus(state.typeAt(index));
},
child: SmartTextField(
type: state.typeAt(index),
controller: state.textAt(index),
focusNode: state.nodeAt(index),
)
);
}
);
}
)

Note: We used the Focus widget to easily listen to focus changes and update the selectedType of the EditorProvider

注意: 我们使用的 Focus 窗口小部件轻松收听集中修改和更新 selectedType 中的 EditorProvider

创建和删除文本块 (Creating & Deleting Text Blocks)

Okay, now that we updated our state management and text editor to store and display a list of text blocks, we need to implement the logic of creating and deleting blocks. If the user presses enter, we need to create a new TextField and switch the focus to it. If the user presses the backspace key to remove a text block, we need to erase the text or merge it with the text block above.

好的,现在我们更新了状态管理和文本编辑器以存储和显示文本块列表,我们需要实现创建和删除块的逻辑。 如果用户按下Enter键,我们需要创建一个新的TextField并将焦点切换到它。 如果用户按退格键删除文本块,则需要擦除文本或将其与上方的文本块合并。

We will first go over the logic and code snippets on how to handle the two events. Afterward, I’ll show you the full code you need to copy into the insert method of the EditorProvider . So bear with me!

我们将首先介绍如何处理这两个事件的逻辑和代码片段。 之后,我将向您展示需要复制到EditorProviderinsert方法中的完整代码。 所以忍受我!

Handling the Remove Event

处理删除事件

To detect the remove event, we’ll use a small trick. We use the zero-width space Unicode character \u200B as a reference for the start of a new line via

为了检测remove事件,我们将使用一个小技巧。 我们使用零宽度空格Unicode字符\u200B作为参考,通过以下行开始新行

final TextEditingController controller = TextEditingController(
text: '\u200B' + (text?? '')
);

If the user then presses the backspace key and removes the starting character, i.e. \u200B , we detect a remove event, delete the focused text block, and move the focus to the text block above.

如果用户然后按下Backspace键并删除开始字符,即\u200B ,则我们将检测到remove事件,删除已聚焦的文本块,然后将焦点移至上方的文本块。

if (!controller.text.startsWith('\u200B')) {
final int index = _text.indexOf(controller);
if (index > 0) {
textAt(index-1).text += controller.text;
nodeAt(index-1).requestFocus();
_text.removeAt(index);
_nodes.removeAt(index);
_types.removeAt(index);
}
}

Note: We concatenate the text of the two text blocks via += controller.text so that the user can easily merge two separate blocks into one.

注意: 我们通过 += controller.text 连接两个文本块的文本, 以便用户可以轻松地将两个单独的块合并为一个。

Handling the Enter Event

处理Enter事件

Next, we need to detect when the user pressed the Enter key. Since our TextFields use the multiline keyboard type (i.e. keyboardType: TextInputType.multiline ), we can check if the TextEditingController contains the Unicode character \n which represents a line break. If it does, we gonna split the text and move the part after the \n character to the next text block that we create on-the-go. This approach is very important, as it allows the user to split an existing text block into two parts. Here you can see the implementation of the logic described above.

接下来,我们需要检测用户何时按下Enter键。 由于我们的TextFields使用多行键盘类型(即keyboardType: TextInputType.multiline ),因此我们可以检查TextEditingController包含表示换行符的Unicode字符\n 。 如果是这样,我们将分割文本并将\n字符之后的部分移动到我们在移动中创建的下一个文本块中。 这种方法非常重要,因为它允许用户将现有的文本块分为两部分。 在这里,您可以看到上述逻辑的实现。

if(controller.text.contains('\n')) {
final int index = _text.indexOf(controller);
List<String> _split = controller.text.split('\n');
controller.text = _split.first;
insert(
index: index+1,
text: _split.last
);
nodeAt(index+1).requestFocus();
}

When pressing enter on a bullet point, it is very intuitive that another one appears below. So, we modify the call of the insert function a bit, as you can see in the following snippet.

在项目符号点上按Enter键时,很直观地在下面显示另一个。 因此,我们对insert函数的调用进行了一些修改,如下面的代码片段所示。

insert(
index: index+1,
text: _split.last,
type: typeAt(index) == SmartTextType.BULLET
? SmartTextType.BULLET
: SmartTextType.T
);

The Implementation

实施

Combining the above steps leads us to the following implementation of the TextEditingController and Listener that we can paste into the beginning of the insert function of the EditorProvider.

结合上述步骤,我们可以实现TextEditingControllerListener的以下实现,可以将其粘贴到EditorProvider.insert函数的EditorProvider.

addListener
addListener

隐藏键盘工具栏 (Hiding the Keyboard Toolbar)

While we only implemented a simplified version of the text editor from the Medium mobile app, there is one thing that we can easily improve. In the medium app, the toolbar is visible at the bottom even if the keyboard is not. This is certainly not a problem, but from a design point of view, not very appealing. But good news, it is easy to fix using the keyboard_visibility package.

虽然我们仅从Medium移动应用程序中实现了文本编辑器的简化版本,但我们可以轻松改进一件事。 在中型应用程序中,即使没有键盘,工具栏也位于底部。 这当然不是问题,但是从设计的角度来看并不是很吸引人。 但好消息是,使用keyboard_visibility包很容易修复。

First, import the keyboard_visibility package to text_editor.dart via

首先,通过以下方式将keyboard_visibility包导入到text_editor.dart

import 'package:keyboard_visibility/keyboard_visibility.dart';

and then add the following to _TextEditorState:

然后将以下内容添加到_TextEditorState

bool showToolbar = false;@override
void initState() {
super.initState();
KeyboardVisibilityNotification().addNewListener(
onChange: (isVisible) {
setState(() {
showToolbar = isVisible;
});
},
);
}@override
void dispose() {
KeyboardVisibilityNotification().dispose();
super.dispose();
}

Then, also in _TextEditorState , wrap the second Positioned widget with the following if-statement:

然后,也在_TextEditorState ,使用以下if语句包装第二个Positioned小部件:

if (showToolbar) Positioned(
bottom: 0,
...

And, that’s it!

而且,就是这样!

完整代码 (Full Code)

You can find the full source code of the implementation here.

您可以在此处找到实现的完整源代码

翻译自: https://levelup.gitconnected.com/flutter-medium-like-text-editor-b41157f50f0e

css 颤动

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值