Browse Source

Initial

main
lucas cardoso 2 years ago
parent
commit
cb955d8117
  1. 4
      Dockerfile
  2. 16
      README.md
  3. 29
      analysis_options.yaml
  4. 36
      docker-compose.yaml
  5. 32
      hasura/docker-compose.yaml
  6. 10
      lib/app_theme.dart
  7. 35
      lib/create.sql
  8. 30
      lib/main.dart
  9. 176
      lib/modules/forms/domain/models/ficha_cadastral_model.dart
  10. 72
      lib/modules/forms/presenter/controllers/form_ficha_controller.dart
  11. 29
      lib/modules/forms/presenter/controllers/form_list_controller.dart
  12. 56
      lib/modules/forms/presenter/ui/atoms/field_form.dart
  13. 61
      lib/modules/forms/presenter/ui/atoms/radio_form.dart
  14. 66
      lib/modules/forms/presenter/ui/atoms/radio_form_bool.dart
  15. 35
      lib/modules/forms/presenter/ui/atoms/title_header.dart
  16. 366
      lib/modules/forms/presenter/ui/pages/form_ficha.dart
  17. 59
      lib/modules/forms/presenter/ui/pages/form_list_page.dart
  18. 12
      lib/modules/home/presenter/controllers/home_controller.dart
  19. 39
      lib/modules/home/presenter/ui/pages/home_page.dart
  20. 127
      lib/modules/home/presenter/ui/template/template_main.dart
  21. 92
      pubspec.yaml
  22. 30
      test/widget_test.dart
  23. BIN
      web/favicon.png
  24. BIN
      web/icons/Icon-192.png
  25. BIN
      web/icons/Icon-512.png
  26. BIN
      web/icons/Icon-maskable-192.png
  27. BIN
      web/icons/Icon-maskable-512.png
  28. 58
      web/index.html
  29. 35
      web/manifest.json
  30. 17
      windows/.gitignore
  31. 101
      windows/CMakeLists.txt
  32. 104
      windows/flutter/CMakeLists.txt
  33. 11
      windows/flutter/generated_plugin_registrant.cc
  34. 15
      windows/flutter/generated_plugin_registrant.h
  35. 23
      windows/flutter/generated_plugins.cmake
  36. 32
      windows/runner/CMakeLists.txt
  37. 121
      windows/runner/Runner.rc
  38. 61
      windows/runner/flutter_window.cpp
  39. 33
      windows/runner/flutter_window.h
  40. 43
      windows/runner/main.cpp
  41. 16
      windows/runner/resource.h
  42. BIN
      windows/runner/resources/app_icon.ico
  43. 20
      windows/runner/runner.exe.manifest
  44. 64
      windows/runner/utils.cpp
  45. 19
      windows/runner/utils.h
  46. 245
      windows/runner/win32_window.cpp
  47. 98
      windows/runner/win32_window.h

4
Dockerfile

@ -0,0 +1,4 @@
# Stage 2 - Create the run-time image
FROM nginx:1.23.0-alpine
COPY /build/web/ /usr/share/nginx/html
EXPOSE 80

16
README.md

@ -0,0 +1,16 @@
# formulario_simples
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

29
analysis_options.yaml

@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

36
docker-compose.yaml

@ -0,0 +1,36 @@
version: '3.6'
services:
app:
build: .
ports:
- 80:80
postgres:
image: postgres:12
restart: always
volumes:
- db_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgrespassword
graphql-engine:
image: hasura/graphql-engine:v2.8.3
ports:
- "8082:8080"
depends_on:
- "postgres"
restart: always
environment:
## postgres database to store Hasura metadata
HASURA_GRAPHQL_METADATA_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
## this env var can be used to add the above postgres database to Hasura as a data source. this can be removed/updated based on your needs
PG_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
## enable the console served by server
HASURA_GRAPHQL_ENABLE_CONSOLE: "true" # set to "false" to disable console
## enable debugging mode. It is recommended to disable this in production
HASURA_GRAPHQL_DEV_MODE: "true"
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
## uncomment next line to set an admin secret
# HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey
volumes:
db_data:

32
hasura/docker-compose.yaml

@ -0,0 +1,32 @@
version: '3.6'
services:
postgres:
image: postgres:12
restart: always
volumes:
- db_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgrespassword
graphql-engine:
image: hasura/graphql-engine:v2.8.3
ports:
- "8082:8080"
depends_on:
- "postgres"
restart: always
environment:
## postgres database to store Hasura metadata
HASURA_GRAPHQL_METADATA_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
## this env var can be used to add the above postgres database to Hasura as a data source. this can be removed/updated based on your needs
PG_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
## enable the console served by server
HASURA_GRAPHQL_ENABLE_CONSOLE: "true" # set to "false" to disable console
## enable debugging mode. It is recommended to disable this in production
HASURA_GRAPHQL_DEV_MODE: "true"
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
## uncomment next line to set an admin secret
# HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey
volumes:
db_data:

10
lib/app_theme.dart

@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class AppTheme {
static dynamic colorprimary = const Color(0xff006BFF);
static dynamic colorsecondary = const Color(0xff8A8888);
static dynamic colortext = const Color(0xff424141);
static dynamic coloraccent = const Color(0xffFF0071);
static dynamic color2c46225 = const Color(0xff5BDBFB);
static dynamic color03904ad = const Color(0xffFFFFFF);
}

35
lib/create.sql

@ -0,0 +1,35 @@
CREATE TABLE ficha_cadastral (
id SERIAL NOT NULL PRIMARY KEY,
status varchar NOT NULL,
reterINSS boolean NULL,
reterIR boolean NULL,
razaoSocial varchar NULL,
nomeFantasia varchar NULL,
cnpj varchar NULL,
inscricaoEstadual varchar NULL,
inscricaoMunicipal varchar NULL,
ramoAtividade varchar NULL,
telefone varchar NULL,
whatsApp varchar NULL,
site varchar NULL,
regimeFiscal int NULL,
contribuicaoICMS int NULL,
reterISS boolean NULL,
nomeDiretor varchar NULL,
telefoneDiretor varchar NULL,
emailDiretor varchar NULL,
nomeTecnico varchar NULL,
telefoneTecnico varchar NULL,
emailTecnico varchar NULL,
nomeFinanceiro varchar NULL,
telefoneFinanceiro varchar NULL,
emailFinanceiro varchar NULL,
emailParaNotaFiscal varchar NULL,
rua varchar NULL,
bairro varchar NULL,
numero varchar NULL,
cidade varchar NULL,
cep varchar NULL,
estado varchar NULL,
complemento varchar NULL
);

30
lib/main.dart

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/pages/form_ficha.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/pages/form_list_page.dart';
import 'package:formulario_simples/modules/home/presenter/ui/pages/home_page.dart';
import 'package:url_strategy/url_strategy.dart';
void main() {
runApp(const MyApp());
setPathUrlStrategy();
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Form Simples',
debugShowCheckedModeBanner: false,
themeMode: ThemeMode.light,
theme: ThemeData.light(),
routes: {
'/': (context) => const FormFichaPage(),
FormListPage.route: (context) => const FormListPage(),
FormFichaPage.route: (context) => const FormFichaPage(),
},
);
}
}

176
lib/modules/forms/domain/models/ficha_cadastral_model.dart

@ -0,0 +1,176 @@
abstract class RadioValue {
String get title => '';
int get value => 0;
}
enum RegimeFiscal implements RadioValue {
lucroReal(title: 'Lucro Real', value: 0),
lucroPresumido(title: 'Lucro Presumido', value: 1),
simplesNacional(title: 'Simples Nacional', value: 2);
@override
final String title;
@override
final int value;
const RegimeFiscal({required this.title, required this.value});
}
enum ContribuicaoICMS implements RadioValue {
contribuinte(title: 'Contribuinte', value: 0),
naoContribuinte(title: 'Não Contribuinte', value: 1),
isento(title: 'Isento', value: 2);
@override
final String title;
@override
final int value;
const ContribuicaoICMS({required this.title, required this.value});
}
class FichaCadastralModel {
static get collectionId => 'ficha_cadastral';
int? id;
String status;
String razaoSocial;
String nomeFantasia;
String cnpj;
String inscricaoEstadual;
String inscricaoMunicipal;
String ramoAtividade;
String telefone;
String whatsApp;
String site;
int? regimeFiscal;
int? contribuicaoICMS;
bool? reterISS;
bool? reterINSS;
bool? reterIR;
String nomeDiretor;
String telefoneDiretor;
String emailDiretor;
String nomeTecnico;
String telefoneTecnico;
String emailTecnico;
String nomeFinanceiro;
String telefoneFinanceiro;
String emailFinanceiro;
String emailParaNotaFiscal;
String rua;
String bairro;
String numero;
String cidade;
String cep;
String estado;
String complemento;
FichaCadastralModel({
this.id,
this.status = 'validado',
this.razaoSocial = '',
this.nomeFantasia = '',
this.cnpj = '',
this.inscricaoEstadual = '',
this.inscricaoMunicipal = '',
this.ramoAtividade = '',
this.telefone = '',
this.whatsApp = '',
this.site = '',
this.regimeFiscal,
this.contribuicaoICMS,
this.reterISS,
this.nomeDiretor = '',
this.telefoneDiretor = '',
this.emailDiretor = '',
this.nomeTecnico = '',
this.telefoneTecnico = '',
this.emailTecnico = '',
this.nomeFinanceiro = '',
this.telefoneFinanceiro = '',
this.emailFinanceiro = '',
this.emailParaNotaFiscal = '',
this.rua = '',
this.bairro = '',
this.numero = '',
this.cidade = '',
this.cep = '',
this.estado = '',
this.complemento = '',
this.reterINSS,
this.reterIR,
});
FichaCadastralModel.fromJson(dynamic json)
: razaoSocial = json['razaosocial'],
status = json['status'],
nomeFantasia = json['nomefantasia'],
cnpj = json['cnpj'],
inscricaoEstadual = json['inscricaoestadual'],
inscricaoMunicipal = json['inscricaomunicipal'],
ramoAtividade = json['ramoatividade'],
telefone = json['telefone'],
whatsApp = json['whatsApp'] ?? '',
site = json['site'],
regimeFiscal = json['regimefiscal'],
contribuicaoICMS = json['contribuicaoicms'],
reterISS = json['reteriss'],
nomeDiretor = json['nomediretor'],
telefoneDiretor = json['telefonediretor'],
emailDiretor = json['emaildiretor'],
nomeTecnico = json['nometecnico'],
telefoneTecnico = json['telefonetecnico'],
emailTecnico = json['emailtecnico'],
nomeFinanceiro = json['nomefinanceiro'],
telefoneFinanceiro = json['telefonefinanceiro'],
emailFinanceiro = json['emailfinanceiro'],
emailParaNotaFiscal = json['emailparanotafiscal'],
rua = json['rua'],
bairro = json['bairro'],
numero = json['numero'],
cidade = json['cidade'],
cep = json['cep'],
estado = json['estado'],
complemento = json['complemento'],
reterINSS = json['reterinss'],
reterIR = json['reterir'],
id = json['id'];
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (id != null) {
data['id'] = id;
}
data['status'] = status;
data['razaosocial'] = razaoSocial;
data['nomefantasia'] = nomeFantasia;
data['cnpj'] = cnpj;
data['inscricaoestadual'] = inscricaoEstadual;
data['inscricaomunicipal'] = inscricaoMunicipal;
data['ramoatividade'] = ramoAtividade;
data['telefone'] = telefone;
data['whatsapp'] = whatsApp;
data['site'] = site;
data['regimefiscal'] = regimeFiscal;
data['contribuicaoicms'] = contribuicaoICMS;
data['reteriss'] = reterISS;
data['nomediretor'] = nomeDiretor;
data['telefonediretor'] = telefoneDiretor;
data['emaildiretor'] = emailDiretor;
data['nometecnico'] = nomeTecnico;
data['telefonetecnico'] = telefoneTecnico;
data['emailtecnico'] = emailTecnico;
data['nomefinanceiro'] = nomeFinanceiro;
data['telefonefinanceiro'] = telefoneFinanceiro;
data['emailfinanceiro'] = emailFinanceiro;
data['emailparanotafiscal'] = emailParaNotaFiscal;
data['rua'] = rua;
data['bairro'] = bairro;
data['numero'] = numero;
data['cidade'] = cidade;
data['cep'] = cep;
data['estado'] = estado;
data['complemento'] = complemento;
data['reterinss'] = reterINSS;
data['reterir'] = reterIR;
return data;
}
}

72
lib/modules/forms/presenter/controllers/form_ficha_controller.dart

@ -0,0 +1,72 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:formulario_simples/modules/forms/domain/models/ficha_cadastral_model.dart';
import 'package:http/http.dart';
enum StatusBuild { done, loading, error }
class FormFichaController {
final FocusNode noteFocus = FocusNode();
final formKey = GlobalKey<FormState>();
FichaCadastralModel ficha = FichaCadastralModel();
var status = ValueNotifier(StatusBuild.loading);
FormFichaController();
void dispose() {}
void init(BuildContext context) {
var arguments = ModalRoute.of(context)!.settings.arguments;
if (arguments != null) {
ficha = arguments as FichaCadastralModel;
}
status.value = StatusBuild.done;
}
Future<void> createFichaCadastral(context) async {
if (formKey.currentState!.validate()) {
// If the form is valid, display a snackbar. In the real world,
// you'd often call a server or save the information in a database.
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
var cli = Client();
var body = {"object1": ficha.toJson()};
var ret = await cli.post(
Uri.parse('http://localhost:8082/api/rest/ficha_cadastral'),
body: json.encode(body),
);
var jsonRet = json.decode(ret.body) as Map;
if (jsonRet.containsKey('insert_ficha_cadastral_one')) {
showDialog(
context: context,
builder: (context) => AlertDialog(
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('Ok'),
),
],
buttonPadding: const EdgeInsets.all(8),
title: const Text('Formulário enviado com sucesso'),
),
);
} else {
showDialog(
context: context,
builder: (context) => AlertDialog(
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('Ok'),
),
],
buttonPadding: const EdgeInsets.all(8),
title: const Text('Erro ao enviar o formulário'),
),
);
}
}
}
}

29
lib/modules/forms/presenter/controllers/form_list_controller.dart

@ -0,0 +1,29 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:formulario_simples/modules/forms/domain/models/ficha_cadastral_model.dart';
import 'package:http/http.dart';
class FormListController {
List<FichaCadastralModel> lista = [];
FormListController();
void Function(void Function() fn) att = (v) => null;
void dispose() {}
void init(BuildContext context) {
var arguments = ModalRoute.of(context)!.settings.arguments;
carregaFichas();
}
Future<void> carregaFichas() async {
var cli = Client();
var ret = await cli.get(
Uri.parse('http://localhost:8082/api/rest/ficha_cadastral'),
);
List map = json.decode(ret.body)['ficha_cadastral'];
lista = map.map((e) => FichaCadastralModel.fromJson(e)).toList();
att(() => print('att'));
}
}

56
lib/modules/forms/presenter/ui/atoms/field_form.dart

@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
class FieldForm extends StatelessWidget {
final String title;
final bool isNotValida;
final String? initialValue;
final void Function(String)? onChanged;
const FieldForm({
super.key,
required this.title,
this.onChanged,
this.isNotValida = false,
required this.initialValue,
});
@override
Widget build(BuildContext context) {
final FocusNode noteFocus = FocusNode();
print(initialValue);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
Text(
title,
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 5),
TextFormField(
initialValue: initialValue,
onChanged: onChanged,
focusNode: noteFocus,
validator: (v) {
if (isNotValida) {
return null;
}
if (v!.isEmpty) {
noteFocus.requestFocus();
return 'O campo $title não pode está vazio';
}
return null;
},
decoration: InputDecoration(
filled: true,
hintText: 'Digite a $title',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(18),
gapPadding: 5,
),
),
),
const SizedBox(height: 10),
],
);
}
}

61
lib/modules/forms/presenter/ui/atoms/radio_form.dart

@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import 'package:formulario_simples/modules/forms/domain/models/ficha_cadastral_model.dart';
class RadioForm extends StatelessWidget {
final String title;
final List<RadioValue> items;
final RadioValue? value;
final double? widthItem;
final void Function(RadioValue?)? onChanged;
const RadioForm({
super.key,
required this.title,
required this.items,
this.value,
this.onChanged,
this.widthItem,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
Text(
title,
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 5),
Container(
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(18),
border: Border.all(
width: 0.8,
),
),
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Wrap(
children: items
.map(
(e) => SizedBox(
width: widthItem ?? 200,
child: RadioListTile<RadioValue>(
title: Text(
e.title,
style: Theme.of(context).textTheme.bodyMedium,
),
value: e,
groupValue: value,
onChanged: onChanged,
),
),
)
.toList(),
),
),
const SizedBox(height: 10),
],
);
}
}

66
lib/modules/forms/presenter/ui/atoms/radio_form_bool.dart

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
class RadioFormBool extends StatelessWidget {
final String title;
final bool? value;
final double? widthItem;
final void Function(bool?)? onChanged;
const RadioFormBool({
super.key,
required this.title,
this.value = false,
this.onChanged,
this.widthItem = 110,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
Text(
title,
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 5),
Container(
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(18),
border: Border.all(
width: 0.8,
),
),
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Wrap(
children: [
SizedBox(
width: widthItem,
child: RadioListTile<bool>(
value: true,
title: Text(
'Sim',
style: Theme.of(context).textTheme.bodyMedium,
),
groupValue: value,
onChanged: onChanged,
)),
SizedBox(
width: widthItem,
child: RadioListTile<bool>(
value: false,
title: Text(
'Não',
style: Theme.of(context).textTheme.bodyMedium,
),
groupValue: value,
onChanged: onChanged,
),
),
],
)),
const SizedBox(height: 10),
],
);
}
}

35
lib/modules/forms/presenter/ui/atoms/title_header.dart

@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:formulario_simples/app_theme.dart';
class TitleHeader extends StatelessWidget {
final String title;
const TitleHeader({super.key, required this.title});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
Container(
margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: AppTheme.colorprimary,
borderRadius: BorderRadius.circular(16),
),
child: Center(
child: Text(
title,
style: Theme.of(context).textTheme.titleLarge!.copyWith(color: Colors.white),
),
),
),
],
),
const Divider(
thickness: 3,
)
],
);
}
}

366
lib/modules/forms/presenter/ui/pages/form_ficha.dart

@ -0,0 +1,366 @@
import 'package:flutter/material.dart';
import 'package:formulario_simples/app_theme.dart';
import 'package:formulario_simples/modules/forms/domain/models/ficha_cadastral_model.dart';
import 'package:formulario_simples/modules/forms/presenter/controllers/form_ficha_controller.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/atoms/field_form.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/atoms/radio_form.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/atoms/radio_form_bool.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/atoms/title_header.dart';
import 'package:formulario_simples/modules/home/presenter/ui/template/template_main.dart';
class FormFichaPage extends StatefulWidget {
static const route = '/FormFicha';
const FormFichaPage({Key? key}) : super(key: key);
@override
State<FormFichaPage> createState() => _FormFichaPageState();
}
class _FormFichaPageState extends State<FormFichaPage> {
final ct = FormFichaController();
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) => ct.init(context));
super.initState();
}
@override
void dispose() {
ct.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
var width = MediaQuery.of(context).size.width;
var height = MediaQuery.of(context).size.height;
var tex = width / 200;
print(tex);
return ValueListenableBuilder(
valueListenable: ct.status,
builder: (context, v, n) {
if (ct.status.value == StatusBuild.loading) {
return const TemplateMain(
selectedIndex: 0,
child: Center(
child: CircularProgressIndicator(),
),
);
}
return TemplateMain(
selectedIndex: 0,
floatingActionButton: FloatingActionButton.extended(
backgroundColor: AppTheme.colorprimary,
onPressed: () => ct.createFichaCadastral(context),
label: Text(
'Enviar dados',
style: Theme.of(context).textTheme.titleLarge!.copyWith(color: Colors.white),
),
),
child: Expanded(
child: Column(
children: [
const TitleHeader(title: 'Ficha Cadastral - Simples IP'),
Expanded(
child: Form(
key: ct.formKey,
child: ListView(
cacheExtent: height * 10,
padding: EdgeInsets.symmetric(horizontal: 16 * tex),
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Dados Cadastrais',
style: Theme.of(context).textTheme.titleLarge,
),
],
),
const SizedBox(height: 5),
FieldForm(
initialValue: ct.ficha.razaoSocial,
title: 'Razão Social',
onChanged: (v) => ct.ficha.razaoSocial = v,
),
FieldForm(
initialValue: ct.ficha.nomeFantasia,
title: 'Nome Fantasia',
onChanged: (v) => ct.ficha.nomeFantasia = v,
),
FieldForm(
title: 'CNPJ',
initialValue: ct.ficha.cnpj,
onChanged: (v) => ct.ficha.cnpj = v,
),
Row(
children: [
Expanded(
child: FieldForm(
initialValue: ct.ficha.cnpj,
title: 'Inscrição Estadual',
onChanged: (v) => ct.ficha.inscricaoEstadual = v,
isNotValida: true,
),
),
const SizedBox(width: 10),
Expanded(
child: FieldForm(
initialValue: ct.ficha.inscricaoMunicipal,
title: 'Inscrição Municipal',
onChanged: (v) => ct.ficha.inscricaoMunicipal = v,
isNotValida: true,
),
),
],
),
FieldForm(
initialValue: ct.ficha.ramoAtividade,
title: 'Ramo de Atividade',
onChanged: (v) => ct.ficha.ramoAtividade = v,
),
Row(
children: [
Expanded(
child: FieldForm(
initialValue: ct.ficha.telefone,
title: 'Telefone',
onChanged: (v) => ct.ficha.telefone = v,
),
),
const SizedBox(width: 10),
Expanded(
child: FieldForm(
initialValue: ct.ficha.whatsApp,
title: 'WhatsApp',
onChanged: (v) => ct.ficha.whatsApp = v,
),
),
],
),
FieldForm(
title: 'Site',
initialValue: ct.ficha.site,
onChanged: (v) => ct.ficha.site = v,
isNotValida: true,
),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Dados Fiscais',
style: Theme.of(context).textTheme.titleLarge,
),
],
),
RadioForm(
title: 'Regime Fiscal',
items: RegimeFiscal.values,
onChanged: (v) => setState(() => ct.ficha.regimeFiscal = v!.value),
value: ct.ficha.regimeFiscal == null
? null
: RegimeFiscal.values.elementAt(ct.ficha.regimeFiscal!),
),
RadioForm(
title: 'Contribuição ICMS',
items: ContribuicaoICMS.values,
onChanged: (v) => setState(() => ct.ficha.contribuicaoICMS = v!.value),
value: ct.ficha.contribuicaoICMS == null
? null
: ContribuicaoICMS.values.elementAt(ct.ficha.contribuicaoICMS!),
widthItem: 180,
),
Wrap(
children: [
RadioFormBool(
title: 'Reter ISS',
onChanged: (v) => setState(() => ct.ficha.reterISS = v),
value: ct.ficha.reterISS,
),
const SizedBox(width: 10),
RadioFormBool(
title: 'Reter INSS',
onChanged: (v) => setState(() => ct.ficha.reterINSS = v),
value: ct.ficha.reterINSS,
),
const SizedBox(width: 10),
RadioFormBool(
title: 'Reter IR',
onChanged: (v) => setState(() => ct.ficha.reterIR = v),
value: ct.ficha.reterIR,
),
],
),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Contatos',
style: Theme.of(context).textTheme.titleLarge,
),
],
),
FieldForm(
title: 'Nome diretor',
initialValue: ct.ficha.nomeDiretor,
onChanged: (v) => ct.ficha.nomeDiretor = v,
),
Row(
children: [
Expanded(
child: FieldForm(
initialValue: ct.ficha.telefoneDiretor,
title: 'Telefone diretor',
onChanged: (v) => ct.ficha.telefoneDiretor = v,
),
),
const SizedBox(width: 10),
Expanded(
child: FieldForm(
initialValue: ct.ficha.emailDiretor,
title: 'E-mail diretor',
onChanged: (v) => ct.ficha.emailDiretor = v,
),
),
],
),
FieldForm(
title: 'Nome técnico',
initialValue: ct.ficha.nomeTecnico,
onChanged: (v) => ct.ficha.nomeTecnico = v,
),
Row(
children: [
Expanded(
child: FieldForm(
title: 'Telefone técnico',
initialValue: ct.ficha.telefoneTecnico,
onChanged: (v) => ct.ficha.telefoneTecnico = v,
),
),
const SizedBox(width: 10),
Expanded(
child: FieldForm(
initialValue: ct.ficha.emailTecnico,
title: 'E-mail técnico',
onChanged: (v) => ct.ficha.emailTecnico = v,
),
),
],
),
FieldForm(
title: 'Nome Financeiro',
initialValue: ct.ficha.nomeFinanceiro,
onChanged: (v) => ct.ficha.nomeFinanceiro = v,
),
Row(
children: [
Expanded(
child: FieldForm(
title: 'Telefone Financeiro',
initialValue: ct.ficha.telefoneFinanceiro,
onChanged: (v) => ct.ficha.telefoneFinanceiro = v,
),
),
const SizedBox(width: 10),
Expanded(
child: FieldForm(
title: 'E-mail Financeiro',
initialValue: ct.ficha.emailFinanceiro,
onChanged: (v) => ct.ficha.emailFinanceiro = v,
),
),
],
),
FieldForm(
title: 'E-mails para Nota Fiscal',
initialValue: ct.ficha.emailParaNotaFiscal,
onChanged: (v) => ct.ficha.emailParaNotaFiscal = v,
),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Endereço',
style: Theme.of(context).textTheme.titleLarge,
),
],
),
const SizedBox(height: 5),
Wrap(
children: [
SizedBox(
width: (width / 2) - (38 * tex),
child: FieldForm(
title: 'Rua/Av.',
initialValue: ct.ficha.rua,
onChanged: (v) => ct.ficha.rua = v,
),
),
const SizedBox(width: 10),
SizedBox(
width: (width / 2) - (38 * tex),
child: FieldForm(
title: 'Bairro',
initialValue: ct.ficha.bairro,
onChanged: (v) => ct.ficha.bairro = v,
),
),
const SizedBox(width: 10),
SizedBox(
width: 70,
child: FieldForm(
title: '',
initialValue: ct.ficha.numero,
onChanged: (v) => ct.ficha.numero = v,
),
),
],
),
Row(
children: [
Expanded(
child: FieldForm(
title: 'Cidade.',
initialValue: ct.ficha.cidade,
onChanged: (v) => ct.ficha.cidade = v,
),
),
const SizedBox(width: 10),
Expanded(
child: FieldForm(
title: 'CEP',
initialValue: ct.ficha.cep,
onChanged: (v) => ct.ficha.cep = v,
),
),
const SizedBox(width: 10),
Expanded(
child: FieldForm(
title: 'Estado',
initialValue: ct.ficha.estado,
onChanged: (v) => ct.ficha.estado = v,
),
),
],
),
FieldForm(
title: 'Complemento',
isNotValida: true,
initialValue: ct.ficha.complemento,
onChanged: (v) => ct.ficha.complemento = v,
),
],
),
),
),
],
),
),
);
});
}
}

59
lib/modules/forms/presenter/ui/pages/form_list_page.dart

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:formulario_simples/app_theme.dart';
import 'package:formulario_simples/modules/forms/presenter/controllers/form_list_controller.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/atoms/title_header.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/pages/form_ficha.dart';
import 'package:formulario_simples/modules/home/presenter/ui/template/template_main.dart';
class FormListPage extends StatefulWidget {
static const route = '/FormList';
const FormListPage({Key? key}) : super(key: key);
@override
State<FormListPage> createState() => _FormListPageState();
}
class _FormListPageState extends State<FormListPage> {
final ct = FormListController();
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) => ct.init(context));
super.initState();
}
@override
void dispose() {
ct.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
ct.att = setState;
return TemplateMain(
selectedIndex: 1,
child: Expanded(
child: Column(
children: [
const TitleHeader(title: 'Formularios'),
Expanded(
child: ListView.builder(
itemCount: ct.lista.length,
padding: const EdgeInsets.symmetric(horizontal: 16),
itemBuilder: (context, index) {
var ficha = ct.lista[index];
return ListTile(
onTap: () => Navigator.pushNamed(context, FormFichaPage.route, arguments: ficha),
title: Text(ficha.nomeFantasia),
subtitle: Text(ficha.cnpj),
);
},
),
)
],
),
),
);
}
}

12
lib/modules/home/presenter/controllers/home_controller.dart

@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
class HomeController {
int selectedIndex = 0;
HomeController();
void dispose() {}
void init(BuildContext context) {
var arguments = ModalRoute.of(context)!.settings.arguments;
}
}

39
lib/modules/home/presenter/ui/pages/home_page.dart

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:formulario_simples/app_theme.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/pages/form_ficha.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/pages/form_list_page.dart';
import 'package:formulario_simples/modules/home/presenter/controllers/home_controller.dart';
import 'package:formulario_simples/modules/home/presenter/ui/template/template_main.dart';
class HomePage extends StatefulWidget {
static const route = '/Home';
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final ct = HomeController();
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) => ct.init(context));
super.initState();
}
@override
void dispose() {
ct.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return const TemplateMain(
child: Text('Home'),
selectedIndex: null,
);
// This is the main content.;
}
}

127
lib/modules/home/presenter/ui/template/template_main.dart

@ -0,0 +1,127 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:formulario_simples/app_theme.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/pages/form_ficha.dart';
import 'package:formulario_simples/modules/forms/presenter/ui/pages/form_list_page.dart';
class TemplateMain extends StatelessWidget {
final int? selectedIndex;
final Widget child;
final Widget? floatingActionButton;
final Function(int)? onDestinationSelected;
const TemplateMain({
super.key,
required this.child,
this.onDestinationSelected,
required this.selectedIndex,
this.floatingActionButton,
});
@override
Widget build(BuildContext context) {
if (kIsWeb) {
return Scaffold(
floatingActionButton: floatingActionButton,
body: Row(
children: [
child,
],
),
);
}
return Scaffold(
appBar: MediaQuery.of(context).size.width < 800
? AppBar(
backgroundColor: AppTheme.colorprimary,
)
: null,
drawer: Row(
children: [
NavigationRail(
selectedIndex: selectedIndex,
onDestinationSelected: (v) {
switch (v) {
case 0:
Navigator.pushReplacementNamed(context, FormFichaPage.route);
break;
case 1:
Navigator.pushReplacementNamed(context, FormListPage.route);
break;
default:
}
},
leading: Padding(
padding: const EdgeInsets.all(16.0),
child: SvgPicture.network(
'https://simplesip.net.br/wp-content/uploads/2022/06/ic-simples.svg',
width: 60,
height: 60,
color: AppTheme.colorprimary,
),
),
labelType: NavigationRailLabelType.selected,
selectedLabelTextStyle: Theme.of(context).textTheme.titleMedium,
destinations: const <NavigationRailDestination>[
NavigationRailDestination(
icon: Icon(Icons.format_align_center),
selectedIcon: Icon(Icons.format_align_center_sharp),
label: Text('Respostas'),
),
NavigationRailDestination(
icon: Icon(Icons.format_bold),
selectedIcon: Icon(Icons.format_bold_sharp),
label: Text('Formularios'),
),
],
),
],
),
floatingActionButton: floatingActionButton,
body: Row(
children: <Widget>[
MediaQuery.of(context).size.width > 800
? NavigationRail(
selectedIndex: selectedIndex,
onDestinationSelected: (v) {
switch (v) {
case 0:
Navigator.pushReplacementNamed(context, FormFichaPage.route);
break;
case 1:
Navigator.pushReplacementNamed(context, FormListPage.route);
break;
default:
}
},
leading: Padding(
padding: const EdgeInsets.all(16.0),
child: SvgPicture.network(
'https://simplesip.net.br/wp-content/uploads/2022/06/ic-simples.svg',
width: 60,
height: 60,
color: AppTheme.colorprimary,
),
),
labelType: NavigationRailLabelType.selected,
selectedLabelTextStyle: Theme.of(context).textTheme.titleMedium,
destinations: const <NavigationRailDestination>[
NavigationRailDestination(
icon: Icon(Icons.format_align_center),
selectedIcon: Icon(Icons.format_align_center_sharp),
label: Text('Respostas'),
),
NavigationRailDestination(
icon: Icon(Icons.format_bold),
selectedIcon: Icon(Icons.format_bold_sharp),
label: Text('Formularios'),
),
],
)
: SizedBox(),
const VerticalDivider(thickness: 1, width: 1),
child,
],
),
);
}
}

92
pubspec.yaml

@ -0,0 +1,92 @@
name: formulario_simples
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.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.17.5 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
flutter_svg: ^1.1.1+1
http: ^0.13.4
url_strategy: ^0.2.0
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.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:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, 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 from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages

30
test/widget_test.dart

@ -0,0 +1,30 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:formulario_simples/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}

BIN
web/favicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

BIN
web/icons/Icon-192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
web/icons/Icon-512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
web/icons/Icon-maskable-192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
web/icons/Icon-maskable-512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

58
web/index.html

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- 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="formulario_simples">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>formulario_simples</title>
<link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
}
}).then(function(engineInitializer) {
return engineInitializer.initializeEngine();
}).then(function(appRunner) {
return appRunner.runApp();
});
});
</script>
</body>
</html>

35
web/manifest.json

@ -0,0 +1,35 @@
{
"name": "formulario_simples",
"short_name": "formulario_simples",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

17
windows/.gitignore vendored

@ -0,0 +1,17 @@
flutter/ephemeral/
# Visual Studio user-specific files.
*.suo
*.user
*.userosscache
*.sln.docstates
# Visual Studio build-related files.
x64/
x86/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

101
windows/CMakeLists.txt

@ -0,0 +1,101 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.14)
project(formulario_simples 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 "formulario_simples")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# 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()
# 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)

104
windows/flutter/CMakeLists.txt

@ -0,0 +1,104 @@
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.14)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"flutter_export.h"
"flutter_windows.h"
"flutter_messenger.h"
"flutter_plugin_registrar.h"
"flutter_texture_registrar.h"
)
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
add_dependencies(flutter flutter_assemble)
# === Wrapper ===
list(APPEND CPP_WRAPPER_SOURCES_CORE
"core_implementations.cc"
"standard_codec.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
"plugin_registrar.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_APP
"flutter_engine.cc"
"flutter_view_controller.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
# Wrapper sources needed for a plugin.
add_library(flutter_wrapper_plugin STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
)
apply_standard_settings(flutter_wrapper_plugin)
set_target_properties(flutter_wrapper_plugin PROPERTIES
POSITION_INDEPENDENT_CODE ON)
set_target_properties(flutter_wrapper_plugin PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
target_include_directories(flutter_wrapper_plugin PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_plugin flutter_assemble)
# Wrapper sources needed for the runner.
add_library(flutter_wrapper_app STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_APP}
)
apply_standard_settings(flutter_wrapper_app)
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
target_include_directories(flutter_wrapper_app PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_app flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
${PHONY_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)

11
windows/flutter/generated_plugin_registrant.cc

@ -0,0 +1,11 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
void RegisterPlugins(flutter::PluginRegistry* registry) {
}

15
windows/flutter/generated_plugin_registrant.h

@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter/plugin_registry.h>
// Registers Flutter plugins.
void RegisterPlugins(flutter::PluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

23
windows/flutter/generated_plugins.cmake

@ -0,0 +1,23 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

32
windows/runner/CMakeLists.txt

@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.14)
project(runner LANGUAGES CXX)
# Define the application target. To change its name, change BINARY_NAME in the
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
# work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
"utils.cpp"
"win32_window.cpp"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
"Runner.rc"
"runner.exe.manifest"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Disable Windows macros that collide with C++ standard library functions.
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
# Add dependency libraries and include directories. Add any application-specific
# dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)

121
windows/runner/Runner.rc

@ -0,0 +1,121 @@
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APP_ICON ICON "resources\\app_icon.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
#ifdef FLUTTER_BUILD_NUMBER
#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
#else
#define VERSION_AS_NUMBER 1,0,0
#endif
#ifdef FLUTTER_BUILD_NAME
#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
#else
#define VERSION_AS_STRING "1.0.0"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_AS_NUMBER
PRODUCTVERSION VERSION_AS_NUMBER
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "FileDescription", "formulario_simples" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "formulario_simples" "\0"
VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0"
VALUE "OriginalFilename", "formulario_simples.exe" "\0"
VALUE "ProductName", "formulario_simples" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

61
windows/runner/flutter_window.cpp

@ -0,0 +1,61 @@
#include "flutter_window.h"
#include <optional>
#include "flutter/generated_plugin_registrant.h"
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
: project_(project) {}
FlutterWindow::~FlutterWindow() {}
bool FlutterWindow::OnCreate() {
if (!Win32Window::OnCreate()) {
return false;
}
RECT frame = GetClientArea();
// The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
}
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
return true;
}
void FlutterWindow::OnDestroy() {
if (flutter_controller_) {
flutter_controller_ = nullptr;
}
Win32Window::OnDestroy();
}
LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
// Give Flutter, including plugins, an opportunity to handle window messages.
if (flutter_controller_) {
std::optional<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
lparam);
if (result) {
return *result;
}
}
switch (message) {
case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts();
break;
}
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
}

33
windows/runner/flutter_window.h

@ -0,0 +1,33 @@
#ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <memory>
#include "win32_window.h"
// A window that does nothing but host a Flutter view.
class FlutterWindow : public Win32Window {
public:
// Creates a new FlutterWindow hosting a Flutter view running |project|.
explicit FlutterWindow(const flutter::DartProject& project);
virtual ~FlutterWindow();
protected:
// Win32Window:
bool OnCreate() override;
void OnDestroy() override;
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
LPARAM const lparam) noexcept override;
private:
// The project to run.
flutter::DartProject project_;
// The Flutter instance hosted by this window.
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
};
#endif // RUNNER_FLUTTER_WINDOW_H_

43
windows/runner/main.cpp

@ -0,0 +1,43 @@
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
#include "flutter_window.h"
#include "utils.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
_In_ wchar_t *command_line, _In_ int show_command) {
// Attach to console when present (e.g., 'flutter run') or create a
// new console when running with a debugger.
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
CreateAndAttachConsole();
}
// Initialize COM, so that it is available for use in the library and/or
// plugins.
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
flutter::DartProject project(L"data");
std::vector<std::string> command_line_arguments =
GetCommandLineArguments();
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"formulario_simples", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::CoUninitialize();
return EXIT_SUCCESS;
}

16
windows/runner/resource.h

@ -0,0 +1,16 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Runner.rc
//
#define IDI_APP_ICON 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

BIN
windows/runner/resources/app_icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

20
windows/runner/runner.exe.manifest

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>

64
windows/runner/utils.cpp

@ -0,0 +1,64 @@
#include "utils.h"
#include <flutter_windows.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>
#include <iostream>
void CreateAndAttachConsole() {
if (::AllocConsole()) {
FILE *unused;
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2);
}
std::ios::sync_with_stdio();
FlutterDesktopResyncOutputStreams();
}
}
std::vector<std::string> GetCommandLineArguments() {
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
int argc;
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
if (argv == nullptr) {
return std::vector<std::string>();
}
std::vector<std::string> command_line_arguments;
// Skip the first argument as it's the binary name.
for (int i = 1; i < argc; i++) {
command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
}
::LocalFree(argv);
return command_line_arguments;
}
std::string Utf8FromUtf16(const wchar_t* utf16_string) {
if (utf16_string == nullptr) {
return std::string();
}
int target_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, nullptr, 0, nullptr, nullptr);
std::string utf8_string;
if (target_length == 0 || target_length > utf8_string.max_size()) {
return utf8_string;
}
utf8_string.resize(target_length);
int converted_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, utf8_string.data(),
target_length, nullptr, nullptr);
if (converted_length == 0) {
return std::string();
}
return utf8_string;
}

19
windows/runner/utils.h

@ -0,0 +1,19 @@
#ifndef RUNNER_UTILS_H_
#define RUNNER_UTILS_H_
#include <string>
#include <vector>
// Creates a console for the process, and redirects stdout and stderr to
// it for both the runner and the Flutter library.
void CreateAndAttachConsole();
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
// encoded in UTF-8. Returns an empty std::string on failure.
std::string Utf8FromUtf16(const wchar_t* utf16_string);
// Gets the command line arguments passed in as a std::vector<std::string>,
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments();
#endif // RUNNER_UTILS_H_

245
windows/runner/win32_window.cpp

@ -0,0 +1,245 @@
#include "win32_window.h"
#include <flutter_windows.h>
#include "resource.h"
namespace {
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
// Scale helper to convert logical scaler values to physical using passed in
// scale factor
int Scale(int source, double scale_factor) {
return static_cast<int>(source * scale_factor);
}
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
// This API is only needed for PerMonitor V1 awareness mode.
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
HMODULE user32_module = LoadLibraryA("User32.dll");
if (!user32_module) {
return;
}
auto enable_non_client_dpi_scaling =
reinterpret_cast<EnableNonClientDpiScaling*>(
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr) {
enable_non_client_dpi_scaling(hwnd);
FreeLibrary(user32_module);
}
}
} // namespace
// Manages the Win32Window's window class registration.
class WindowClassRegistrar {
public:
~WindowClassRegistrar() = default;
// Returns the singleton registar instance.
static WindowClassRegistrar* GetInstance() {
if (!instance_) {
instance_ = new WindowClassRegistrar();
}
return instance_;
}
// Returns the name of the window class, registering the class if it hasn't
// previously been registered.
const wchar_t* GetWindowClass();
// Unregisters the window class. Should only be called if there are no
// instances of the window.
void UnregisterWindowClass();
private:
WindowClassRegistrar() = default;
static WindowClassRegistrar* instance_;
bool class_registered_ = false;
};
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
const wchar_t* WindowClassRegistrar::GetWindowClass() {
if (!class_registered_) {
WNDCLASS window_class{};
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = kWindowClassName;
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc;
RegisterClass(&window_class);
class_registered_ = true;
}
return kWindowClassName;
}
void WindowClassRegistrar::UnregisterWindowClass() {
UnregisterClass(kWindowClassName, nullptr);
class_registered_ = false;
}
Win32Window::Win32Window() {
++g_active_window_count;
}
Win32Window::~Win32Window() {
--g_active_window_count;
Destroy();
}
bool Win32Window::CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size) {
Destroy();
const wchar_t* window_class =
WindowClassRegistrar::GetInstance()->GetWindowClass();
const POINT target_point = {static_cast<LONG>(origin.x),
static_cast<LONG>(origin.y)};
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
double scale_factor = dpi / 96.0;
HWND window = CreateWindow(
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
nullptr, nullptr, GetModuleHandle(nullptr), this);
if (!window) {
return false;
}
return OnCreate();
}
// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
if (message == WM_NCCREATE) {
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
EnableFullDpiSupportIfAvailable(window);
that->window_handle_ = window;
} else if (Win32Window* that = GetThisFromHandle(window)) {
return that->MessageHandler(window, message, wparam, lparam);
}
return DefWindowProc(window, message, wparam, lparam);
}
LRESULT
Win32Window::MessageHandler(HWND hwnd,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
switch (message) {
case WM_DESTROY:
window_handle_ = nullptr;
Destroy();
if (quit_on_close_) {
PostQuitMessage(0);
}
return 0;
case WM_DPICHANGED: {
auto newRectSize = reinterpret_cast<RECT*>(lparam);
LONG newWidth = newRectSize->right - newRectSize->left;
LONG newHeight = newRectSize->bottom - newRectSize->top;
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
case WM_SIZE: {
RECT rect = GetClientArea();
if (child_content_ != nullptr) {
// Size and position the child window.
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, TRUE);
}
return 0;
}
case WM_ACTIVATE:
if (child_content_ != nullptr) {
SetFocus(child_content_);
}
return 0;
}
return DefWindowProc(window_handle_, message, wparam, lparam);
}
void Win32Window::Destroy() {
OnDestroy();
if (window_handle_) {
DestroyWindow(window_handle_);
window_handle_ = nullptr;
}
if (g_active_window_count == 0) {
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
}
}
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
return reinterpret_cast<Win32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA));
}
void Win32Window::SetChildContent(HWND content) {
child_content_ = content;
SetParent(content, window_handle_);
RECT frame = GetClientArea();
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
frame.bottom - frame.top, true);
SetFocus(child_content_);
}
RECT Win32Window::GetClientArea() {
RECT frame;
GetClientRect(window_handle_, &frame);
return frame;
}
HWND Win32Window::GetHandle() {
return window_handle_;
}
void Win32Window::SetQuitOnClose(bool quit_on_close) {
quit_on_close_ = quit_on_close;
}
bool Win32Window::OnCreate() {
// No-op; provided for subclasses.
return true;
}
void Win32Window::OnDestroy() {
// No-op; provided for subclasses.
}

98
windows/runner/win32_window.h

@ -0,0 +1,98 @@
#ifndef RUNNER_WIN32_WINDOW_H_
#define RUNNER_WIN32_WINDOW_H_
#include <windows.h>
#include <functional>
#include <memory>
#include <string>
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
// inherited from by classes that wish to specialize with custom
// rendering and input handling
class Win32Window {
public:
struct Point {
unsigned int x;
unsigned int y;
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
};
struct Size {
unsigned int width;
unsigned int height;
Size(unsigned int width, unsigned int height)
: width(width), height(height) {}
};
Win32Window();
virtual ~Win32Window();
// Creates and shows a win32 window with |title| and position and size using
// |origin| and |size|. New windows are created on the default monitor. Window
// sizes are specified to the OS in physical pixels, hence to ensure a
// consistent size to will treat the width height passed in to this function
// as logical pixels and scale to appropriate for the default monitor. Returns
// true if the window was created successfully.
bool CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size);
// Release OS resources associated with window.
void Destroy();
// Inserts |content| into the window tree.
void SetChildContent(HWND content);
// Returns the backing Window handle to enable clients to set icon and other
// window properties. Returns nullptr if the window has been destroyed.
HWND GetHandle();
// If true, closing this window will quit the application.
void SetQuitOnClose(bool quit_on_close);
// Return a RECT representing the bounds of the current client area.
RECT GetClientArea();
protected:
// Processes and route salient window messages for mouse handling,
// size change and DPI. Delegates handling of these to member overloads that
// inheriting classes can handle.
virtual LRESULT MessageHandler(HWND window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Called when CreateAndShow is called, allowing subclass window-related
// setup. Subclasses should return false if setup fails.
virtual bool OnCreate();
// Called when Destroy is called.
virtual void OnDestroy();
private:
friend class WindowClassRegistrar;
// OS callback called by message pump. Handles the WM_NCCREATE message which
// is passed when the non-client area is being created and enables automatic
// non-client DPI scaling so that the non-client area automatically
// responsponds to changes in DPI. All other messages are handled by
// MessageHandler.
static LRESULT CALLBACK WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
bool quit_on_close_ = false;
// window handle for top level window.
HWND window_handle_ = nullptr;
// window handle for hosted content.
HWND child_content_ = nullptr;
};
#endif // RUNNER_WIN32_WINDOW_H_
Loading…
Cancel
Save