feat:增加服务器接口模块
This commit is contained in:
11
lib/apis/auth_api.dart
Normal file
11
lib/apis/auth_api.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'package:sweet_chat_app/models/auth.dart';
|
||||
import 'package:sweet_chat_app/services/dio_service.dart';
|
||||
|
||||
Future<Auth> loginApi(String username, String password) async {
|
||||
final res = await dioService.post(
|
||||
'/auth/login',
|
||||
data: {'username': username, 'password': password},
|
||||
);
|
||||
|
||||
return Auth.fromJson(res.data);
|
||||
}
|
||||
10
lib/apis/user_api.dart
Normal file
10
lib/apis/user_api.dart
Normal file
@@ -0,0 +1,10 @@
|
||||
import 'package:sweet_chat_app/models/user.dart';
|
||||
import 'package:sweet_chat_app/services/dio_service.dart';
|
||||
|
||||
Future<List<User>> getUserFriendsApi(int userId) async {
|
||||
final res = await dioService.get('/user/friend/$userId');
|
||||
|
||||
if (res.data == null) return [];
|
||||
|
||||
return (res.data as List).map((e) => User.fromJson(e)).toList();
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
import 'package:sweet_chat_app/pages/login.dart';
|
||||
import 'package:sweet_chat_app/utils/SpUtil.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await SpUtil.init();
|
||||
// 预编译 shader,防止首帧白闪
|
||||
await LiquidGlassWidgets.initialize();
|
||||
// wrap() 安装无障碍桥接、全局主题、自适应质量
|
||||
|
||||
20
lib/models/ImMessage.dart
Normal file
20
lib/models/ImMessage.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
class ImMessage {
|
||||
final String type;
|
||||
final String from;
|
||||
final String to;
|
||||
final String content;
|
||||
|
||||
ImMessage({
|
||||
required this.type,
|
||||
required this.from,
|
||||
required this.to,
|
||||
required this.content,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': type,
|
||||
'from': from,
|
||||
'to': to,
|
||||
'content': content,
|
||||
};
|
||||
}
|
||||
14
lib/models/auth.dart
Normal file
14
lib/models/auth.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'auth.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class Auth {
|
||||
final int userId;
|
||||
|
||||
Auth({required this.userId});
|
||||
|
||||
factory Auth.fromJson(Map<String, dynamic> json) => _$AuthFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$AuthToJson(this);
|
||||
}
|
||||
14
lib/models/auth.g.dart
Normal file
14
lib/models/auth.g.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'auth.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Auth _$AuthFromJson(Map<String, dynamic> json) =>
|
||||
Auth(userId: (json['userId'] as num).toInt());
|
||||
|
||||
Map<String, dynamic> _$AuthToJson(Auth instance) => <String, dynamic>{
|
||||
'userId': instance.userId,
|
||||
};
|
||||
28
lib/models/user.dart
Normal file
28
lib/models/user.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'user.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class User {
|
||||
final int id;
|
||||
final String phone;
|
||||
final String nickname;
|
||||
final String avatarUrl;
|
||||
final int gender;
|
||||
final DateTime createTime;
|
||||
final DateTime updateTime;
|
||||
|
||||
User({
|
||||
required this.id,
|
||||
required this.phone,
|
||||
required this.nickname,
|
||||
required this.avatarUrl,
|
||||
required this.gender,
|
||||
required this.createTime,
|
||||
required this.updateTime,
|
||||
});
|
||||
|
||||
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$UserToJson(this);
|
||||
}
|
||||
27
lib/models/user.g.dart
Normal file
27
lib/models/user.g.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
User _$UserFromJson(Map<String, dynamic> json) => User(
|
||||
id: (json['id'] as num).toInt(),
|
||||
phone: json['phone'] as String,
|
||||
nickname: json['nickname'] as String,
|
||||
avatarUrl: json['avatarUrl'] as String,
|
||||
gender: (json['gender'] as num).toInt(),
|
||||
createTime: DateTime.parse(json['createTime'] as String),
|
||||
updateTime: DateTime.parse(json['updateTime'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'phone': instance.phone,
|
||||
'nickname': instance.nickname,
|
||||
'avatarUrl': instance.avatarUrl,
|
||||
'gender': instance.gender,
|
||||
'createTime': instance.createTime.toIso8601String(),
|
||||
'updateTime': instance.updateTime.toIso8601String(),
|
||||
};
|
||||
@@ -1,5 +1,10 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
import 'package:sweet_chat_app/models/ImMessage.dart';
|
||||
import 'package:sweet_chat_app/services/webSocket_service.dart';
|
||||
import 'package:sweet_chat_app/utils/SpUtil.dart';
|
||||
|
||||
class ChatMessage {
|
||||
final String text;
|
||||
@@ -34,10 +39,45 @@ class _ChatPageState extends State<ChatPage> {
|
||||
super.initState();
|
||||
|
||||
_messages = [
|
||||
ChatMessage(text: '在吗?', isMe: false, avatar: widget.avatarUrl),
|
||||
ChatMessage(text: '在的,怎么啦 😊', isMe: true),
|
||||
ChatMessage(text: '今晚一起吃饭吗?', isMe: false, avatar: widget.avatarUrl),
|
||||
// ChatMessage(text: '在吗?', isMe: false, avatar: widget.avatarUrl),
|
||||
// ChatMessage(text: '在的,怎么啦 😊', isMe: true),
|
||||
// ChatMessage(text: '今晚一起吃饭吗?', isMe: false, avatar: widget.avatarUrl),
|
||||
];
|
||||
|
||||
final userId = SpUtil.getInt('userId') ?? 0;
|
||||
final online = ImMessage(
|
||||
type: "ONLINE",
|
||||
from: userId.toString(),
|
||||
to: '',
|
||||
content: '',
|
||||
);
|
||||
|
||||
WebSocketService().connect('ws://192.168.31.108:5050?userId=$userId');
|
||||
WebSocketService().send(jsonEncode(online.toJson()));
|
||||
|
||||
WebSocketService().messages.listen((msg) {
|
||||
print('收到消息: $msg');
|
||||
|
||||
setState(() {
|
||||
_messages.add(ChatMessage(text: msg, isMe: false, avatar: widget.avatarUrl));
|
||||
});
|
||||
|
||||
_controller.clear();
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
_scrollController.animateTo(
|
||||
_scrollController.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
WebSocketService().disconnect();
|
||||
}
|
||||
|
||||
void _sendMessage() {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import 'package:azlistview/azlistview.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lpinyin/lpinyin.dart';
|
||||
import 'package:sweet_chat_app/apis/user_api.dart';
|
||||
import 'package:sweet_chat_app/models/user.dart';
|
||||
import 'package:sweet_chat_app/utils/SpUtil.dart';
|
||||
|
||||
import 'chat_page.dart';
|
||||
|
||||
class ContactInfo extends ISuspensionBean {
|
||||
final String name;
|
||||
@@ -29,54 +34,39 @@ class ContactsPage extends StatefulWidget {
|
||||
|
||||
class _ContactsPageState extends State<ContactsPage> {
|
||||
List<ContactInfo> contactList = [];
|
||||
List<ContactInfo> topList = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_loadFakeData();
|
||||
_loadFriends();
|
||||
}
|
||||
|
||||
void _loadFakeData() {
|
||||
contactList = [
|
||||
ContactInfo(name: '张三', avatar: 'https://picsum.photos/id/1011/200'),
|
||||
ContactInfo(name: '李四', avatar: 'https://picsum.photos/id/1012/200'),
|
||||
ContactInfo(name: '王五', avatar: 'https://picsum.photos/id/1013/200'),
|
||||
ContactInfo(name: '赵六', avatar: 'https://picsum.photos/id/1014/200'),
|
||||
ContactInfo(name: '陈七', avatar: 'https://picsum.photos/id/1015/200'),
|
||||
ContactInfo(name: '刘八', avatar: 'https://picsum.photos/id/1016/200'),
|
||||
ContactInfo(name: '周九', avatar: 'https://picsum.photos/id/1017/200'),
|
||||
ContactInfo(name: '吴十', avatar: 'https://picsum.photos/id/1018/200'),
|
||||
ContactInfo(name: '欧阳峰', avatar: 'https://picsum.photos/id/1021/200'),
|
||||
ContactInfo(name: '诸葛青', avatar: 'https://picsum.photos/id/1022/200'),
|
||||
ContactInfo(name: '司马光', avatar: 'https://picsum.photos/id/1023/200'),
|
||||
ContactInfo(name: '慕容复', avatar: 'https://picsum.photos/id/1024/200'),
|
||||
ContactInfo(name: '安琪拉', avatar: 'https://picsum.photos/id/1031/200'),
|
||||
ContactInfo(name: '亚瑟', avatar: 'https://picsum.photos/id/1032/200'),
|
||||
ContactInfo(name: '李白', avatar: 'https://picsum.photos/id/1033/200'),
|
||||
ContactInfo(name: '韩信', avatar: 'https://picsum.photos/id/1034/200'),
|
||||
ContactInfo(name: '孙尚香', avatar: 'https://picsum.photos/id/1035/200'),
|
||||
ContactInfo(name: '鲁班', avatar: 'https://picsum.photos/id/1036/200'),
|
||||
ContactInfo(name: '妲己', avatar: 'https://picsum.photos/id/1037/200'),
|
||||
ContactInfo(name: '甄姬', avatar: 'https://picsum.photos/id/1038/200'),
|
||||
];
|
||||
Future<void> _loadFriends() async {
|
||||
final userId = SpUtil.getInt('userId') ?? 0;
|
||||
final friendList = await getUserFriendsApi(userId);
|
||||
|
||||
_handleList(contactList);
|
||||
setState(() {
|
||||
_handleList(friendList);
|
||||
});
|
||||
}
|
||||
|
||||
void _handleList(List<ContactInfo> list) {
|
||||
for (var item in list) {
|
||||
final pinyin = PinyinHelper.getPinyinE(item.name);
|
||||
final tag = pinyin.substring(0, 1).toUpperCase();
|
||||
item.namePinyin = pinyin;
|
||||
item.tagIndex = RegExp(r'[A-Z]').hasMatch(tag) ? tag : '#';
|
||||
}
|
||||
void _handleList(List<User> friends) {
|
||||
contactList =
|
||||
friends.map((user) {
|
||||
final pinyin = PinyinHelper.getPinyinE(user.nickname);
|
||||
final tag = pinyin.substring(0, 1).toUpperCase();
|
||||
|
||||
SuspensionUtil.sortListBySuspensionTag(list);
|
||||
SuspensionUtil.setShowSuspensionStatus(list);
|
||||
return ContactInfo(
|
||||
name: user.nickname,
|
||||
avatar: user.avatarUrl,
|
||||
tagIndex: RegExp(r'^[A-Z]$').hasMatch(tag) ? tag : '#',
|
||||
namePinyin: pinyin,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
setState(() {});
|
||||
SuspensionUtil.sortListBySuspensionTag(contactList);
|
||||
SuspensionUtil.setShowSuspensionStatus(contactList);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -112,18 +102,17 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||
data: contactList,
|
||||
itemCount: contactList.length,
|
||||
itemBuilder: (_, index) {
|
||||
final c = contactList[index];
|
||||
return Container(
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundImage: NetworkImage(c.avatar),
|
||||
onBackgroundImageError: (_, __) {},
|
||||
),
|
||||
title: Text(
|
||||
c.name,
|
||||
style: const TextStyle(fontSize: 16, color: Colors.white),
|
||||
),
|
||||
final contact = contactList[index];
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundImage: NetworkImage(contact.avatar),
|
||||
onBackgroundImageError: (_, __) {},
|
||||
),
|
||||
title: Text(
|
||||
contact.name,
|
||||
style: const TextStyle(fontSize: 16, color: Colors.white),
|
||||
),
|
||||
onTap: () => onTapContact(contact),
|
||||
);
|
||||
},
|
||||
susItemBuilder: (_, index) {
|
||||
@@ -167,4 +156,17 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onTapContact(ContactInfo contact) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(_) => ChatPage(
|
||||
contactName: contact.name,
|
||||
avatarUrl: contact.avatar,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
import 'package:sweet_chat_app/apis/auth_api.dart';
|
||||
import 'package:sweet_chat_app/utils/SpUtil.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
|
||||
import '../services/webSocket_service.dart';
|
||||
@@ -16,21 +19,33 @@ class _LoginPageState extends State<LoginPage> {
|
||||
final _usernameController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
|
||||
void _login() {
|
||||
print('username: ${_usernameController.text}');
|
||||
print('password: ${_passwordController.text}');
|
||||
void _login() async {
|
||||
try {
|
||||
final auth = await loginApi(_usernameController.text, _passwordController.text);
|
||||
await SpUtil.setInt('userId', auth.userId);
|
||||
|
||||
WebSocketService().connect('ws://127.0.0.1:5050?userId=123');
|
||||
|
||||
WebSocketService().messages.listen((msg) {
|
||||
print('收到消息: $msg');
|
||||
});
|
||||
|
||||
// Navigator.of(context).push(
|
||||
// MaterialPageRoute(
|
||||
// builder: (_) => const HomePage(),
|
||||
// ),
|
||||
// );
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const HomePage(),
|
||||
),
|
||||
);
|
||||
|
||||
Fluttertoast.showToast(
|
||||
msg: "登录成功",
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.TOP,
|
||||
backgroundColor: Colors.white,
|
||||
textColor: Colors.black,
|
||||
);
|
||||
} catch (e) {
|
||||
Fluttertoast.showToast(
|
||||
msg: "登录失败",
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.TOP,
|
||||
backgroundColor: Colors.white,
|
||||
textColor: Colors.black,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
7
lib/services/dio_service.dart
Normal file
7
lib/services/dio_service.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
final dioService = Dio(BaseOptions(
|
||||
baseUrl: 'http://192.168.31.108:2836/api',
|
||||
connectTimeout: Duration(seconds: 10),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
));
|
||||
23
lib/utils/SpUtil.dart
Normal file
23
lib/utils/SpUtil.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SpUtil {
|
||||
static late SharedPreferences _sp;
|
||||
|
||||
static Future<void> init() async {
|
||||
_sp = await SharedPreferences.getInstance();
|
||||
}
|
||||
|
||||
static Future<bool> setString(String key, String value) =>
|
||||
_sp.setString(key, value);
|
||||
|
||||
static String? getString(String key) => _sp.getString(key);
|
||||
|
||||
static Future<bool> setInt(String key, int value) =>
|
||||
_sp.setInt(key, value);
|
||||
|
||||
static int? getInt(String key) => _sp.getInt(key);
|
||||
|
||||
static Future<bool> remove(String key) => _sp.remove(key);
|
||||
|
||||
static Future<bool> clear() => _sp.clear();
|
||||
}
|
||||
Reference in New Issue
Block a user