...
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins-dependencies
|
||||
/build/
|
||||
/coverage/
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
|
||||
channel: "stable"
|
||||
|
||||
project_type: package
|
||||
@@ -0,0 +1,3 @@
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
||||
@@ -0,0 +1 @@
|
||||
TODO: Add your license here.
|
||||
@@ -0,0 +1,39 @@
|
||||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/to/develop-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
||||
@@ -0,0 +1,4 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@@ -0,0 +1,31 @@
|
||||
// lib/game/base_game.dart
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 모든 미니게임의 부모 클래스
|
||||
abstract class BaseGame {
|
||||
/// 게임 고유 ID (예: 'quiz', 'bomb') - 패킷 라우팅용
|
||||
String get id;
|
||||
|
||||
/// 게임 이름 (로비 표시용)
|
||||
String get name;
|
||||
|
||||
/// 게임 설명
|
||||
String get description;
|
||||
|
||||
/// [Host] 방장이 보는 게임 화면 빌드
|
||||
Widget buildHostView(BuildContext context);
|
||||
|
||||
/// [Guest] 참가자가 보는 게임 화면 빌드
|
||||
Widget buildGuestView(BuildContext context);
|
||||
|
||||
/// 게임이 시작될 때 초기화 로직 (변수 초기화 등)
|
||||
void onStart();
|
||||
|
||||
/// 네트워크 메시지 수신 처리
|
||||
/// [senderId]: 보낸 사람 ID
|
||||
/// [payload]: 수신된 데이터 (JSON)
|
||||
void onMessageReceived(String senderId, Map<String, dynamic> payload);
|
||||
|
||||
/// 게임 종료 및 메모리 정리
|
||||
void onDispose();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class UserInfo extends Equatable {
|
||||
final String id;
|
||||
final String nickname;
|
||||
final int avatarIndex; // 프로필 이미지 대신 사용할 아바타 번호 (0~9 등)
|
||||
final int colorValue; // 유저 고유 컬러 (ARGB int)
|
||||
|
||||
const UserInfo({
|
||||
required this.id,
|
||||
required this.nickname,
|
||||
this.avatarIndex = 0,
|
||||
this.colorValue = 0xFF2196F3, // 기본값 Blue
|
||||
});
|
||||
|
||||
/// JSON -> Object 변환 (네트워크 수신 시)
|
||||
factory UserInfo.fromJson(Map<String, dynamic> json) {
|
||||
return UserInfo(
|
||||
id: json['id'] as String,
|
||||
nickname: json['nickname'] as String,
|
||||
avatarIndex: json['avatarIndex'] as int? ?? 0,
|
||||
colorValue: json['colorValue'] as int? ?? 0xFF2196F3,
|
||||
);
|
||||
}
|
||||
|
||||
/// Object -> JSON 변환 (네트워크 전송 시)
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'nickname': nickname,
|
||||
'avatarIndex': avatarIndex,
|
||||
'colorValue': colorValue,
|
||||
};
|
||||
}
|
||||
|
||||
/// 복사본 생성 (불변 객체 수정용)
|
||||
UserInfo copyWith({
|
||||
String? id,
|
||||
String? nickname,
|
||||
int? avatarIndex,
|
||||
int? colorValue,
|
||||
}) {
|
||||
return UserInfo(
|
||||
id: id ?? this.id,
|
||||
nickname: nickname ?? this.nickname,
|
||||
avatarIndex: avatarIndex ?? this.avatarIndex,
|
||||
colorValue: colorValue ?? this.colorValue,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [id, nickname, avatarIndex, colorValue];
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bonsoir/bonsoir.dart'; // mDNS 패키지
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../model/user_info.dart';
|
||||
|
||||
/// 현재 네트워크 상태 (역할)
|
||||
enum NetworkRole { none, host, guest }
|
||||
|
||||
/// P2P 네트워크 통신을 담당하는 싱글톤 매니저
|
||||
class NetworkManager extends ChangeNotifier {
|
||||
// ------------------------------------------------------------------------
|
||||
// 1. Singleton & Initialization
|
||||
// ------------------------------------------------------------------------
|
||||
static final NetworkManager _instance = NetworkManager._internal();
|
||||
factory NetworkManager() => _instance;
|
||||
NetworkManager._internal();
|
||||
|
||||
/// 내 정보 (앱 시작 시 initialize 호출 필수)
|
||||
late UserInfo me;
|
||||
|
||||
/// 현재 내 역할
|
||||
NetworkRole role = NetworkRole.none;
|
||||
|
||||
/// 초기화 메서드 (닉네임 설정 및 ID 생성)
|
||||
void initialize({required String nickname}) {
|
||||
// 8자리 랜덤 ID 생성
|
||||
final uuid = const Uuid().v4().substring(0, 8);
|
||||
// 랜덤 컬러 (간단하게 해시코드로 생성 예시)
|
||||
final randomColor = 0xFF000000 | (nickname.hashCode & 0xFFFFFF);
|
||||
|
||||
me = UserInfo(
|
||||
id: uuid,
|
||||
nickname: nickname,
|
||||
colorValue: randomColor,
|
||||
);
|
||||
print('[Network] Initialized User: ${me.nickname} (${me.id})');
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 2. Variables & Streams
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// 소켓
|
||||
ServerSocket? _serverSocket; // (Host용)
|
||||
Socket? _clientSocket; // (Guest용)
|
||||
final List<Socket> _connectedGuests = []; // (Host가 관리하는 게스트 목록)
|
||||
|
||||
// mDNS (방 찾기/만들기)
|
||||
BonsoirService? _bonsoirService;
|
||||
BonsoirBroadcast? _bonsoirBroadcast;
|
||||
BonsoirDiscovery? _bonsoirDiscovery;
|
||||
|
||||
// 수신된 데이터를 앱(GameManager)으로 전달하는 스트림
|
||||
final _messageController = StreamController<Map<String, dynamic>>.broadcast();
|
||||
Stream<Map<String, dynamic>> get messageStream => _messageController.stream;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 3. Host Logic (방장)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 방 만들기
|
||||
Future<void> startHosting(String roomName) async {
|
||||
stopNetwork(); // 기존 연결 정리
|
||||
role = NetworkRole.host;
|
||||
|
||||
try {
|
||||
// A. TCP 서버 소켓 오픈 (Port 0 = 시스템 자동 할당)
|
||||
_serverSocket = await ServerSocket.bind(InternetAddress.anyIPv4, 0);
|
||||
int port = _serverSocket!.port;
|
||||
print('[Host] Server opened on port: $port');
|
||||
|
||||
// B. 게스트 접속 대기
|
||||
_serverSocket!.listen((Socket client) {
|
||||
_handleNewGuest(client);
|
||||
});
|
||||
|
||||
// C. mDNS로 방 광고 (Broadcast)
|
||||
// 서비스 타입은 고유해야 함 (_playwith._tcp)
|
||||
// 이름 포맷: "방이름#호스트ID" (중복 방지 및 식별용)
|
||||
_bonsoirService = BonsoirService(
|
||||
name: '$roomName#${me.id}',
|
||||
type: '_playwith._tcp',
|
||||
port: port,
|
||||
);
|
||||
|
||||
_bonsoirBroadcast = BonsoirBroadcast(service: _bonsoirService!);
|
||||
await _bonsoirBroadcast!.ready;
|
||||
await _bonsoirBroadcast!.start();
|
||||
|
||||
print('[Host] Start advertising room: $roomName');
|
||||
notifyListeners();
|
||||
|
||||
} catch (e) {
|
||||
print('[Host] Error starting host: $e');
|
||||
stopNetwork();
|
||||
}
|
||||
}
|
||||
|
||||
/// 새로운 게스트가 접속했을 때
|
||||
void _handleNewGuest(Socket client) {
|
||||
print('[Host] New guest connected: ${client.remoteAddress.address}');
|
||||
_connectedGuests.add(client);
|
||||
|
||||
// 데이터 수신 리스너 부착
|
||||
client.listen(
|
||||
(Uint8List data) => _onDataReceived(client, data),
|
||||
onError: (e) => _removeGuest(client),
|
||||
onDone: () => _removeGuest(client),
|
||||
);
|
||||
}
|
||||
|
||||
void _removeGuest(Socket client) {
|
||||
print('[Host] Guest disconnected');
|
||||
_connectedGuests.remove(client);
|
||||
client.close();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 4. Guest Logic (참가자)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 주변 방 찾기 (mDNS Discovery)
|
||||
Stream<List<BonsoirService>> discoverRooms() {
|
||||
// 리스트를 계속 갱신해서 내보내기 위한 컨트롤러
|
||||
final controller = StreamController<List<BonsoirService>>();
|
||||
final List<BonsoirService> foundServices = [];
|
||||
|
||||
_bonsoirDiscovery = BonsoirDiscovery(type: '_playwith._tcp');
|
||||
|
||||
_bonsoirDiscovery!.ready.then((_) {
|
||||
_bonsoirDiscovery!.start();
|
||||
|
||||
_bonsoirDiscovery!.eventStream!.listen((event) {
|
||||
if (event.type == BonsoirDiscoveryEventType.discoveryServiceFound) {
|
||||
if (event.service != null) {
|
||||
foundServices.add(event.service!);
|
||||
controller.add(List.from(foundServices));
|
||||
}
|
||||
} else if (event.type == BonsoirDiscoveryEventType.discoveryServiceLost) {
|
||||
if (event.service != null) {
|
||||
foundServices.removeWhere((s) => s.name == event.service!.name);
|
||||
controller.add(List.from(foundServices));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return controller.stream;
|
||||
}
|
||||
|
||||
/// 방 접속하기
|
||||
Future<void> joinRoom(String ip, int port) async {
|
||||
stopNetwork(); // 기존 연결 정리
|
||||
role = NetworkRole.guest;
|
||||
|
||||
try {
|
||||
print('[Guest] Connecting to $ip:$port...');
|
||||
_clientSocket = await Socket.connect(ip, port);
|
||||
print('[Guest] Connected!');
|
||||
|
||||
// 접속 성공 시 즉시 내 정보 전송 (Handshake)
|
||||
sendMessage({
|
||||
'type': 'HANDSHAKE',
|
||||
'senderId': me.id,
|
||||
'payload': me.toJson(),
|
||||
});
|
||||
|
||||
// 데이터 수신 리스너
|
||||
_clientSocket!.listen(
|
||||
(Uint8List data) => _onDataReceived(_clientSocket!, data),
|
||||
onError: (e) {
|
||||
print('[Guest] Connection error: $e');
|
||||
stopNetwork();
|
||||
},
|
||||
onDone: () {
|
||||
print('[Guest] Disconnected by host');
|
||||
stopNetwork();
|
||||
},
|
||||
);
|
||||
|
||||
notifyListeners();
|
||||
|
||||
} catch (e) {
|
||||
print('[Guest] Failed to join: $e');
|
||||
role = NetworkRole.none;
|
||||
notifyListeners();
|
||||
rethrow; // UI에서 에러 처리할 수 있게 던짐
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 5. Common Logic (데이터 송수신)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 메시지 전송
|
||||
void sendMessage(Map<String, dynamic> messageMap) {
|
||||
try {
|
||||
// JSON 변환
|
||||
final jsonString = jsonEncode(messageMap);
|
||||
// 패킷 경계 처리를 위해 끝에 줄바꿈(\n) 추가 (가장 간단한 delimiter)
|
||||
final List<int> data = utf8.encode('$jsonString\n');
|
||||
|
||||
if (role == NetworkRole.host) {
|
||||
// Host는 모든 Guest에게 브로드캐스트
|
||||
for (var socket in _connectedGuests) {
|
||||
socket.add(data);
|
||||
}
|
||||
} else if (role == NetworkRole.guest) {
|
||||
// Guest는 Host에게 전송
|
||||
_clientSocket?.add(data);
|
||||
}
|
||||
} catch (e) {
|
||||
print('[Network] Send Error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 데이터 수신 처리
|
||||
void _onDataReceived(Socket socket, Uint8List data) {
|
||||
// 들어온 데이터를 String으로 변환
|
||||
final String rawString = utf8.decode(data);
|
||||
|
||||
// TCP 패킷이 뭉쳐서 올 수 있으므로 \n으로 쪼갬
|
||||
final List<String> splitMessages = rawString.split('\n');
|
||||
|
||||
for (var msg in splitMessages) {
|
||||
if (msg.trim().isEmpty) continue;
|
||||
|
||||
try {
|
||||
final Map<String, dynamic> parsedData = jsonDecode(msg);
|
||||
|
||||
// 1. 앱 로직으로 전달
|
||||
_messageController.add(parsedData);
|
||||
|
||||
// 2. (옵션) Host라면, 받은 메시지를 다른 Guest들에게도 전달(Relay)해야 할 수 있음
|
||||
// 게임 로직에 따라 다르지만 보통 Host가 중계자 역할을 함
|
||||
// if (role == NetworkRole.host) { sendMessage(parsedData); }
|
||||
|
||||
} catch (e) {
|
||||
print('[Network] Parse Error: $e\nMessage: $msg');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 6. Cleanup
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 네트워크 종료 및 리소스 정리
|
||||
void stopNetwork() {
|
||||
print('[Network] Stopping network...');
|
||||
|
||||
// mDNS 중지
|
||||
_bonsoirBroadcast?.stop();
|
||||
_bonsoirDiscovery?.stop();
|
||||
|
||||
// 소켓 닫기
|
||||
_serverSocket?.close();
|
||||
_clientSocket?.close();
|
||||
|
||||
for (var socket in _connectedGuests) {
|
||||
socket.close();
|
||||
}
|
||||
_connectedGuests.clear();
|
||||
|
||||
role = NetworkRole.none;
|
||||
_serverSocket = null;
|
||||
_clientSocket = null;
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// lib/playwith_core.dart
|
||||
|
||||
library playwith_core;
|
||||
|
||||
export 'game/base_game.dart';
|
||||
export 'network/network_manager.dart';
|
||||
export 'model/user_info.dart';
|
||||
@@ -0,0 +1,16 @@
|
||||
name: playwith_core
|
||||
description: Core logic for playWith game platform.
|
||||
version: 0.0.1
|
||||
|
||||
environment:
|
||||
sdk: ^3.0.0
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
# 네트워크 디스커버리 (mDNS)
|
||||
bonsoir: ^2.0.0
|
||||
# 고유 ID 생성
|
||||
uuid: ^4.0.0
|
||||
# 데이터 비교 및 불변성 (선택사항이지만 추천)
|
||||
equatable: ^2.0.5
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:playwith_core/playwith_core.dart';
|
||||
|
||||
void main() {
|
||||
test('adds one to input values', () {
|
||||
final calculator = Calculator();
|
||||
expect(calculator.addOne(2), 3);
|
||||
expect(calculator.addOne(-7), -6);
|
||||
expect(calculator.addOne(0), 1);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins-dependencies
|
||||
/build/
|
||||
/coverage/
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
|
||||
channel: "stable"
|
||||
|
||||
project_type: package
|
||||
@@ -0,0 +1,3 @@
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
||||
@@ -0,0 +1 @@
|
||||
TODO: Add your license here.
|
||||
@@ -0,0 +1,39 @@
|
||||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/to/develop-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
||||
@@ -0,0 +1,4 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@@ -0,0 +1,5 @@
|
||||
/// A Calculator.
|
||||
class Calculator {
|
||||
/// Returns [value] plus 1.
|
||||
int addOne(int value) => value + 1;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
name: playwith_game_quiz
|
||||
description: "A new Flutter package project."
|
||||
version: 0.0.1
|
||||
homepage:
|
||||
|
||||
environment:
|
||||
sdk: ^3.9.2
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
# 코어 패키지 의존성 추가
|
||||
playwith_core:
|
||||
path: ../../core
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^5.0.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# To add assets to your package, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
#
|
||||
# For details regarding assets in packages, see
|
||||
# https://flutter.dev/to/asset-from-package
|
||||
#
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/to/resolution-aware-images
|
||||
|
||||
# To add custom fonts to your package, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts in packages, see
|
||||
# https://flutter.dev/to/font-from-package
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:playwith_game_quiz/playwith_game_quiz.dart';
|
||||
|
||||
void main() {
|
||||
test('adds one to input values', () {
|
||||
final calculator = Calculator();
|
||||
expect(calculator.addOne(2), 3);
|
||||
expect(calculator.addOne(-7), -6);
|
||||
expect(calculator.addOne(0), 1);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user