Compare commits

..

No commits in common. "master" and "lab6" have entirely different histories.
master ... lab6

25 changed files with 635 additions and 175 deletions

View File

@ -1,4 +1,4 @@
# mobilefl
# identity
A new Flutter project.

View File

@ -6,7 +6,7 @@ plugins {
}
android {
namespace = "com.example.mobilefl"
namespace = "com.example.identity"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
@ -21,7 +21,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.mobilefl"
applicationId = "com.example.identity"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion

View File

@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="mobilefl"
android:label="identity"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity

View File

@ -1,4 +1,4 @@
package com.example.mobilefl
package com.example.identity
import io.flutter.embedding.android.FlutterActivity

BIN
assets/images/hunter1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
assets/images/hunter2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
assets/images/hunter3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
assets/images/survivor1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
assets/images/survivor2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
assets/images/survivor3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

55
lib/bloc/bloc.dart Normal file
View File

@ -0,0 +1,55 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'events.dart';
import 'state.dart';
import '../utils/character_service.dart';
import '../models/character.dart';
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
class DebouncedSearchCubit extends Cubit<String> {
DebouncedSearchCubit() : super('');
Timer? _debounce;
// Метод для обновления поиска с задержкой
void search(String query) {
if (_debounce?.isActive ?? false) _debounce?.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
emit(query);
});
}
@override
Future<void> close() {
_debounce?.cancel();
return super.close();
}
}
class HomeBloc extends Bloc<HomeEvent, HomeState> {
final CharacterService characterService;
HomeBloc(this.characterService) : super(const HomeState()) {
on<HomeLoadDataEvent>(_onLoadData);
}
// Внутри HomeBloc, когда получаем данные
Future<void> _onLoadData(HomeLoadDataEvent event, Emitter<HomeState> emit) async {
emit(state.copyWith(status: HomeStatus.loading));
try {
// Получаем список объектов CharacterDTO
final charactersDTO = await characterService.getCharacters(search: event.searchQuery);
// Преобразуем список Character в список CharacterDTO
final charactersAsDTO = charactersDTO.map((character) => character.toDTO()).toList();
emit(state.copyWith(status: HomeStatus.loaded, characters: charactersAsDTO)); // Передаем в emit
} catch (e) {
print('Error: $e');
emit(state.copyWith(status: HomeStatus.error, errorMessage: e.toString()));
}
}
}

17
lib/bloc/events.dart Normal file
View File

@ -0,0 +1,17 @@
import 'package:equatable/equatable.dart';
abstract class HomeEvent extends Equatable {
const HomeEvent();
@override
List<Object?> get props => [];
}
class HomeLoadDataEvent extends HomeEvent {
final String searchQuery;
const HomeLoadDataEvent({this.searchQuery = ''});
@override
List<Object?> get props => [searchQuery];
}

31
lib/bloc/state.dart Normal file
View File

@ -0,0 +1,31 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import '../models/character.dart'; // Добавим импорт для Character
enum HomeStatus { initial, loading, loaded, error }
class HomeState extends Equatable {
final HomeStatus status;
final List<CharacterDTO> characters; // Теперь используем DTO
final String errorMessage;
const HomeState({
this.status = HomeStatus.initial,
this.characters = const [],
this.errorMessage = '',
});
HomeState copyWith({
HomeStatus? status,
List<CharacterDTO>? characters,
String? errorMessage,
}) {
return HomeState(
status: status ?? this.status,
characters: characters ?? this.characters,
errorMessage: errorMessage ?? this.errorMessage,
);
}
@override
List<Object?> get props => [status, characters, errorMessage];
}

View File

@ -1,70 +1,213 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'bloc/bloc.dart';
import 'bloc/events.dart';
import 'bloc/state.dart';
import 'utils/character_service.dart';
import 'models/character.dart';
import 'pages/character_detail_page.dart';
void main() {
runApp(const MyApp());
runApp(
MultiBlocProvider(
providers: [
BlocProvider<HomeBloc>(
create: (_) => HomeBloc(CharacterService()),
),
BlocProvider<DebouncedSearchCubit>(
create: (_) => DebouncedSearchCubit(),
),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const MyHomePage(title: 'Камчарова Ксения Алексеевна'),
title: 'My App',
home: MyHomePage(title: 'Identity'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
class MyHomePage extends StatelessWidget {
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
MyHomePage({required this.title});
@override
Widget build(BuildContext context) {
return Scaffold(
context.read<HomeBloc>().add(HomeLoadDataEvent());
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
title: Text(title),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
showSearch(
context: context,
delegate: CharacterSearchDelegate(),
);
},
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
body: BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
if (state.status == HomeStatus.loading) {
return Center(child: CircularProgressIndicator());
} else if (state.status == HomeStatus.error) {
return Center(child: Text('Ошибка: ${state.errorMessage}'));
} else if (state.status == HomeStatus.loaded) {
final characters = state.characters;
return ListView.builder(
itemCount: characters.length,
itemBuilder: (context, index) {
final characterDTO = characters[index]; // Убедитесь, что это CharacterDTO
return ListTile(
leading: SizedBox(
width: 40,
child: Image.network(characterDTO.imageUrl, width: 50, height: 50),
),
title: Text(characterDTO.name),
subtitle: Text(characterDTO.typeString), // Используем typeString из CharacterDTO
onTap: () {
// Переход на страницу с деталями персонажа, передаем CharacterDTO
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CharacterDetailPage(characterDTO: characterDTO), // Передаем CharacterDTO
),
);
},
);
},
);
} else {
return Center(child: Text('Нет данных.'));
}
},
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), );
);
}
}
class CharacterSearchDelegate extends SearchDelegate {
@override
Widget buildSuggestions(BuildContext context) {
return BlocProvider<HomeBloc>(
create: (context) => HomeBloc(CharacterService()),
child: BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
final suggestions = state.characters
.where((character) => character.name.toLowerCase().contains(query.toLowerCase()))
.toList();
return ListView.builder(
itemCount: suggestions.length,
itemBuilder: (context, index) {
final character = suggestions[index]; // Используем правильный тип
return ListTile(
title: Text(character.name),
subtitle: Text(character.typeString),
onTap: () {
// Переход на страницу с деталями персонажа
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CharacterDetailPage(characterDTO: character), // Передаем правильный параметр
),
);
},
);
},
);
},
),
);
}
@override
Widget buildResults(BuildContext context) {
final debouncedSearchCubit = context.read<DebouncedSearchCubit>();
debouncedSearchCubit.search(query);
return BlocBuilder<DebouncedSearchCubit, String>(
builder: (context, searchQuery) {
if (searchQuery.isNotEmpty) {
context.read<HomeBloc>().add(HomeLoadDataEvent(searchQuery: searchQuery));
}
return BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
if (state.status == HomeStatus.loading) {
return Center(child: CircularProgressIndicator());
} else if (state.status == HomeStatus.error) {
return Center(child: Text('Ошибка: ${state.errorMessage}'));
} else if (state.status == HomeStatus.loaded) {
final characters = state.characters
.where((character) => character.name.toLowerCase().contains(searchQuery.toLowerCase()))
.toList();
return ListView.builder(
itemCount: characters.length,
itemBuilder: (context, index) {
final characterDTO = characters[index]; // Здесь определяем characterDTO
return ListTile(
leading: SizedBox(
width: 40,
child: Image.network(characterDTO.imageUrl, width: 50, height: 50),
),
title: Text(characterDTO.name),
subtitle: Text(characterDTO.typeString), // Используем typeString
onTap: () {
// Переход на страницу с деталями персонажа
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CharacterDetailPage(characterDTO: characterDTO), // Передаем characterDTO
),
);
},
);
}
);
} else {
return Center(child: Text('Нет результатов'));
}
},
);
},
);
}
@override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = '';
showSuggestions(context);
},
),
];
}
@override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
close(context, null);
},
);
}
}

152
lib/models/character.dart Normal file
View File

@ -0,0 +1,152 @@
enum CharacterType { Survivor, Hunter }
class Character {
final String name;
final CharacterType type;
final String backstory;
final String imageUrl;
bool isLiked;
Character({
required this.name,
required this.type,
required this.backstory,
required this.imageUrl,
this.isLiked = false, // Значение по умолчанию - false
});
factory Character.fromJson(Map<String, dynamic> json) {
try {
CharacterType characterType;
if (json['type'] == 'Выживший') {
characterType = CharacterType.Survivor;
} else if (json['type'] == 'Охотник') {
characterType = CharacterType.Hunter;
} else {
throw Exception('Неизвестный тип персонажа: ${json['type']}');
}
return Character(
name: json['name'],
type: characterType,
backstory: json['backstory'],
imageUrl: json['image_url'], // Используем корректный URL
);
} catch (e) {
print("Ошибка при парсинге данных: $e");
throw Exception('Ошибка при парсинге персонажа');
}
}
// Преобразуем тип обратно в строку
String get typeString {
switch (type) {
case CharacterType.Survivor:
return 'Выживший';
case CharacterType.Hunter:
return 'Охотник';
default:
return 'Неизвестно';
}
}
String getInfo() {
return '$typeString - $backstory';
}
// Для пустого объекта
static Character? empty() {
return null; // Возвращаем null для пустого объекта
}
}
class Survivor extends Character {
Survivor({
required String name,
required String backstory,
required String imageUrl,
}) : super(
name: name,
backstory: backstory,
imageUrl: imageUrl,
type: CharacterType.Survivor,
);
}
class Hunter extends Character {
Hunter({
required String name,
required String backstory,
required String imageUrl,
}) : super(
name: name,
backstory: backstory,
imageUrl: imageUrl,
type: CharacterType.Hunter,
);
}
class CharacterDTO {
final String name;
final String characterType;
final String backstory;
final String imageUrl;
CharacterDTO({
required this.name,
required this.characterType,
required this.backstory,
required this.imageUrl,
});
CharacterDTO toDTO() {
return CharacterDTO(
name: name,
characterType: typeString,
backstory: backstory,
imageUrl: imageUrl,
);
}
// Добавим метод fromJson
factory CharacterDTO.fromJson(Map<String, dynamic> json) {
return CharacterDTO(
name: json['name'],
characterType: json['type'], // 'type' - это то, что мы ожидаем от API
backstory: json['backstory'],
imageUrl: json['image_url'], // Используем корректное имя поля для URL
);
}
// Геттер для typeString
String get typeString {
switch (characterType) {
case 'Выживший':
return 'Выживший';
case 'Охотник':
return 'Охотник';
default:
return 'Неизвестно';
}
}
// Преобразуем DTO обратно в объект Character
Character toCharacter() {
return Character(
name: name,
type: characterType == 'Выживший' ? CharacterType.Survivor : CharacterType.Hunter,
backstory: backstory,
imageUrl: imageUrl,
);
}
// Метод для преобразования в Map
Map<String, dynamic> toMap() {
return {
'name': name,
'type': characterType,
'backstory': backstory,
'image_url': imageUrl,
};
}
}

View File

@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import '../models/character.dart'; // Убедитесь, что путь правильный
class CharacterDetailPage extends StatelessWidget {
final CharacterDTO characterDTO;
const CharacterDetailPage({Key? key, required this.characterDTO}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(characterDTO.name),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Image.network(
characterDTO.imageUrl,
height: 200,
width: 200,
fit: BoxFit.cover,
),
),
SizedBox(height: 20),
Text(
characterDTO.name,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 10),
Text(
characterDTO.typeString,
style: TextStyle(fontSize: 18, color: Colors.grey[600]),
),
SizedBox(height: 20),
Text(
'Полное описание:',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
SizedBox(height: 10),
Text(
characterDTO.backstory,
style: TextStyle(fontSize: 14),
maxLines: null,
softWrap: true,
),
],
),
),
);
}
}

View File

@ -0,0 +1,39 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/character.dart';
const String baseUrl = 'http://192.168.1.83:5000'; // IP-адрес вместо localhost
class CharacterService {
// Метод теперь возвращает список CharacterDTO
Future<List<CharacterDTO>> getCharacters({String search = ''}) async {
try {
// Формируем URL с параметром поиска
final uri = Uri.parse('$baseUrl/characters?search=$search');
// Выполняем HTTP-запрос
final response = await http.get(uri);
// Логирование данных для отладки
print('Response status: ${response.statusCode}');
print('Response body: ${response.body}'); // Выводим тело ответа для анализа
// Проверяем успешный ответ от сервера
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
print('Characters received: $data'); // Печать данных
// Возвращаем список объектов CharacterDTO
return data.map((item) => CharacterDTO.fromJson(item)).toList();
} else {
// Ошибка, если сервер вернул не 200 статус
print('Error: Server responded with status ${response.statusCode}');
throw Exception('Ошибка загрузки данных с сервера');
}
} catch (e) {
// Обработка ошибок при выполнении запроса
print('Error fetching characters: $e');
throw Exception('Не удалось загрузить персонажей');
}
}
}

View File

@ -9,6 +9,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.11.0"
bloc:
dependency: transitive
description:
name: bloc
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
url: "https://pub.dev"
source: hosted
version: "8.1.4"
boolean_selector:
dependency: transitive
description:
@ -49,6 +57,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
equatable:
dependency: "direct main"
description:
name: equatable
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
fake_async:
dependency: transitive
description:
@ -62,6 +78,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_bloc:
dependency: "direct main"
description:
name: flutter_bloc
sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
url: "https://pub.dev"
source: hosted
version: "8.1.6"
flutter_lints:
dependency: "direct dev"
description:
@ -75,6 +99,22 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: "direct main"
description:
name: http
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "1.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
leak_tracker:
dependency: transitive
description:
@ -131,6 +171,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.15.0"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
path:
dependency: transitive
description:
@ -139,6 +187,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.0"
provider:
dependency: transitive
description:
name: provider
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
url: "https://pub.dev"
source: hosted
version: "6.1.2"
sky_engine:
dependency: transitive
description: flutter
@ -192,6 +248,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.2"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_math:
dependency: transitive
description:
@ -208,6 +272,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "14.2.5"
web:
dependency: transitive
description:
name: web
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "1.1.0"
sdks:
dart: ">=3.5.2 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"

View File

@ -1,4 +1,6 @@
name: mobilefl
name: identity
description: "A new Flutter project."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
@ -30,8 +32,9 @@ environment:
dependencies:
flutter:
sdk: flutter
http: ^1.2.2
equatable: ^2.0.5
flutter_bloc: ^8.1.5
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
@ -52,6 +55,8 @@ dev_dependencies:
# The following section is specific to Flutter packages.
flutter:
assets:
- assets/images/
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in

View File

@ -8,12 +8,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mobilefl/main.dart';
import 'package:identity/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
await tester.pumpWidget(MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);

View File

@ -23,13 +23,13 @@
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="mobilefl">
<meta name="apple-mobile-web-app-title" content="identity">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>mobilefl</title>
<title>identity</title>
<link rel="manifest" href="manifest.json">
</head>
<body>

View File

@ -1,6 +1,6 @@
{
"name": "mobilefl",
"short_name": "mobilefl",
"name": "identity",
"short_name": "identity",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",

View File

@ -1,108 +0,0 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.14)
project(mobilefl LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "mobilefl")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(VERSION 3.14...3.25)
# Define build configuration option.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG)
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
CACHE STRING "" FORCE)
else()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
endif()
# Define settings for the Profile build mode.
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
# Use Unicode for all projects.
add_definitions(-DUNICODE -D_UNICODE)
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
target_compile_options(${TARGET} PRIVATE /EHsc)
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# Support files are copied into place next to the executable, so that it can
# run in place. This is done instead of making a separate bundle (as on Linux)
# so that building and running from within Visual Studio will work.
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# Make the "install" step default, as it's required to run.
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Copy the native assets provided by the build.dart from all packages.
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/")
install(DIRECTORY "${NATIVE_ASSETS_DIR}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
CONFIGURATIONS Profile;Release
COMPONENT Runtime)

View File

@ -90,12 +90,12 @@ BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "FileDescription", "mobilefl" "\0"
VALUE "FileDescription", "identity" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "mobilefl" "\0"
VALUE "InternalName", "identity" "\0"
VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0"
VALUE "OriginalFilename", "mobilefl.exe" "\0"
VALUE "ProductName", "mobilefl" "\0"
VALUE "OriginalFilename", "identity.exe" "\0"
VALUE "ProductName", "identity" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END

View File

@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.Create(L"mobilefl", origin, size)) {
if (!window.Create(L"identity", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);