用户管理器
创建 Ubuntu Linux Flutter 应用教程,使用 yaru 主题。
简介
你将学到什么
- 在Ubuntu中设置Flutter开发环境
- 使用yaru主题为Linux桌面构建Flutter应用程序
- 使用json-server作为本地后端来存储数据
你将构建什么

设置你的Flutter环境
在Ubuntu上安装Flutter并启用Flutter Linux桌面支持
sudo snap install flutter
flutter channel beta
flutter upgrade
flutter config --enable-linux-desktop
安装和设置Visual Studio Code
要在Ubuntu中安装VS Code,请打开终端并运行以下命令
sudo snap install code --classic
启动VS Code快速打开(Ctrl+P),粘贴以下命令……
ext install Dart-Code.flutter
……然后按Enter键安装官方Flutter VS Code扩展。
开始
打开终端或VS Code集成终端,然后运行以下命令为虚构组织org.flutterfans创建一个新的Flutter项目
flutter create --org org.flutterfans user_manager
用VS Code打开您刚刚创建的项目目录user_manager。

您现在应该看到以下目录(1)以及正在使用的“Linux (linux-x64)”(2)设备,在启用Linux桌面支持后,这在Linux上是默认的。

让我们通过打开lib目录(1)并点击void main()(2)上方的“运行”小标签来测试应用程序是否可以启动。

使用Yaru主题
打开pubspec.yaml - 用于管理应用程序生命周期的文件 - 并在dependencies部分添加yaru: ^0.0.6,这样您将得到以下依赖项(请确保yaru与flutter在同一列)。
dependencies:
flutter:
sdk: flutter
yaru: ^0.0.6
您现在可以通过在main.dart的顶部添加以下内容来导入yaru主题:
import 'package:yaru/yaru.dart' as yaru;
通过将MaterialApp的theme属性设置为yaru.lightTheme,并将darkTheme属性设置为yaru.darkTheme,在您的应用程序中使用yaru主题,这样您将得到以下类定义 - 您的应用程序现在将遵循您系统的外观。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: yaru.lightTheme,
darkTheme: yaru.darkTheme,
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

构建应用程序
组织您的项目结构
您可能需要稍微组织一下项目的目录结构。首先,在lib目录下创建一个view目录,方法是右键单击lib目录,然后单击“新建文件夹”菜单项,并在弹出的文本字段中输入view。

对model和service目录重复此步骤,以便在main.dart之上的目录树中得到以下结构。

一个简单的用户模型
为了方便地在应用程序和服务器之间发送用户数据,您需要一个用户类,其中只包含用户的数据属性。
右键单击model目录,然后单击“新建文件”,并将其命名为user.dart。

插入以下代码
class User {
int? id;
String name;
User({required this.id, required this.name});
User.empty() : this(id: null, name: "");
factory User.fromJson(Map<String, dynamic> json) =>
User(id: json['id'], name: json['name']);
Map<String, dynamic> toJson() => {"id": id, "name": name};
}
用户服务
为了让您的应用程序通过HTTP与您的服务器通信,请在pubspec.yaml的dependencies部分添加http: ^0.13.1,在service目录中创建user_service.dart,并将以下代码粘贴到其中。
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:user_manager/model/user.dart';
class UserService {
UserService({required this.uri});
final String uri;
Future<List<User>> getUsers() async {
final response = await http.get(Uri.http(uri, 'users'));
if (response.statusCode == 200) {
var usersJson = jsonDecode(response.body.toString()) as List;
return usersJson.map((json) => User.fromJson(json)).toList();
} else {
throw Exception(response.statusCode.toString() + ': ' + response.body);
}
}
Future saveUser(User user) async {
for (var aUser in await getUsers()) {
if (aUser.id == user.id) {
await updateUser(user);
return;
}
}
await addUser(user);
}
Future<http.Response> addUser(User user) async {
return await http.post(Uri.http(uri, 'users'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(user.toJson()));
}
Future updateUser(User user) async {
return await http.put(Uri.http(uri, 'users/${user.id}'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(user.toJson()));
}
Future removeUser(User user) async {
await http.delete((Uri.http(uri, 'users/${user.id}')));
}
}
用户界面
每个用户一张卡片
将被管理的每个用户都由UI中的一张卡片表示。
在view目录中创建user_card.dart,并将以下代码粘贴到其中。
import 'package:flutter/material.dart';
import 'package:user_manager/model/user.dart';
class UserCard extends StatefulWidget {
final User user;
final VoidCallback onDelete;
final VoidCallback onEdit;
UserCard({required this.user, required this.onDelete, required this.onEdit});
@override
_UserCardState createState() => _UserCardState();
}
class _UserCardState extends State<UserCard> {
@override
Widget build(BuildContext context) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Icon(Icons.person),
title: Text(widget.user.name),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TextButton(
child: const Text('Edit'),
onPressed: () => widget.onEdit(),
),
const SizedBox(width: 8),
TextButton(
child: const Text('Remove'),
onPressed: () => widget.onDelete(),
),
const SizedBox(width: 8),
],
),
],
),
);
}
}
用于编辑用户的对话框
用户将在对话框中进行编辑,该对话框通过单击巨大的绿色浮动操作按钮或用户卡片的编辑按钮弹出。
在view目录中创建user_edit_dialog.dart文件,并将以下代码复制/粘贴到其中。
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class UserEditDialog extends StatefulWidget {
final TextEditingController nameController;
final AsyncCallback editUser;
UserEditDialog({required this.nameController, required this.editUser});
@override
_UserEditDialogState createState() => _UserEditDialogState();
}
class _UserEditDialogState extends State<UserEditDialog> {
@override
Widget build(BuildContext context) {
return Dialog(
child: Container(
height: 150,
width: 200,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
autofocus: true,
controller: widget.nameController,
decoration: InputDecoration(hintText: 'User name'),
),
),
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () async =>
Navigator.of(context).pop(widget.editUser()),
child: Text(
"Save",
style: TextStyle(color: Colors.white),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("Cancel"),
),
)
],
),
],
),
),
),
);
}
}
新的主页
旧的主页将不足以满足需求,因此您需要一个新的主页来加载用户卡片。主页使用用户服务来管理用户数据。
再次打开您的pubspec.yaml文件,并在dependencies部分添加injector: ^2.0.0。在view目录中创建home_page.dart文件,并将以下代码粘贴进去。
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:injector/injector.dart';
import 'package:user_manager/model/user.dart';
import 'package:user_manager/service/user_service.dart';
import 'package:user_manager/view/user_card.dart';
import 'package:user_manager/view/user_edit_dialog.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late TextEditingController _nameController;
final _userService = Injector.appInstance.get<UserService>();
@override
void initState() {
_nameController = TextEditingController();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Scrollbar(
child: FutureBuilder(
future: _userService.getUsers(),
builder: (context, AsyncSnapshot<List<User>> snapShot) {
if (snapShot.hasData) {
return ListView(
children: snapShot.data!
.map((user) => UserCard(
user: user,
onDelete: () {
_userService
.removeUser(user)
.then((value) => setState(() {
_showSnackBar(
'Deleted user: ' + user.name);
}))
.onError((error, stackTrace) =>
_showSnackBar(error.toString()));
},
onEdit: () => editUser(user, context)))
.toList(),
);
}
return CircularProgressIndicator();
}),
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_nameController.text = "";
editUser(new User.empty(), context);
},
),
);
}
void editUser(User user, BuildContext context) {
_nameController.text = user.name;
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return UserEditDialog(
nameController: _nameController,
editUser: () async {
user.name = _nameController.text;
_userService
.saveUser(user)
.then((value) => setState(() {
_showSnackBar('Saved user: ' + user.name);
}))
.onError(
(error, stackTrace) => _showSnackBar(error.toString()));
});
});
}
void _showSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(duration: const Duration(seconds: 1), content: Text(message)));
}
}
完成您的main.dart
测试应用程序的用户界面不再需要了。删除MyApp以外的所有Widgets,将home属性更改为HomePage(),将您的main()函数更改为异步的,返回一个Future<void>,并在运行应用程序之前使用Injector类注册您的UserService。为了确保您已正确导入所有内容,请用以下新版本替换main.dart中的所有代码。
import 'package:flutter/material.dart';
import 'package:injector/injector.dart';
import 'package:user_manager/service/user_service.dart';
import 'package:user_manager/view/home_page.dart';
import 'package:yaru/yaru.dart' as yaru;
Future<void> main() async {
final userService = UserService(uri: 'localhost:3000');
Injector.appInstance.registerDependency<UserService>(() => userService);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: yaru.lightTheme,
darkTheme: yaru.darkTheme,
home: HomePage(),
);
}
}
创建一个本地服务器来处理客户端请求
创建模拟数据
在应用程序的根文件夹中创建db.json,并在json格式中插入一些虚构用户。
{
"users": [
{
"id": 1,
"name": "Carlo"
},
{
"id": 2,
"name": "Mads"
},
{
"id": 3,
"name": "Frederik"
},
{
"id": 4,
"name": "Stuart"
},
{
"id": 5,
"name": "Paul"
},
{
"id": 6,
"name": "Muq"
}
]
}
安装node-js和json-server
我们将使用一个出色的本地模拟服务器让我们的客户端与之通信。为此,我们需要node snap。输入以下命令来安装node和json-server。
sudo snap install node --classic
sudo npm install -g json-server
进入用户管理 :)
通过运行以下命令来启动您的json-server
json-server --watch db.json
再次启动您的应用程序!完成 :) 您现在可以使用yaru主题在Ubuntu Linux应用程序中管理用户。
