This commit is contained in:
toly
2023-09-22 09:15:11 +08:00
parent d456e3c523
commit e95c34018e
132 changed files with 8527 additions and 17 deletions

3
lib/cases/cases.dart Normal file
View File

@@ -0,0 +1,3 @@
export 'counter/counter_page.dart';
export 'guess/guess_page.dart';
export 'paper/paper.dart';

View File

@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
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,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

View File

@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class GuessAppBar extends StatelessWidget implements PreferredSizeWidget {
final VoidCallback onCheck;
final TextEditingController controller;
const GuessAppBar({
Key? key,
required this.onCheck,
required this.controller,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return AppBar(
leading: Icon(Icons.menu, color: Colors.black),
actions: [
IconButton(
splashRadius: 20,
onPressed: onCheck,
icon: Icon(
Icons.run_circle_outlined,
color: Colors.blue,
))
],
title: TextField(
controller: controller,
keyboardType: TextInputType.number,
decoration: InputDecoration(
filled: true,
fillColor: Color(0xffF3F6F9),
constraints: BoxConstraints(maxHeight: 34), //约束信息
contentPadding: EdgeInsets.only(top: -14,left: 10),
border: UnderlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(6)),
),
hintText: "输入 0~99 数字",
hintStyle: TextStyle(fontSize: 14)),
),
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}

View File

@@ -0,0 +1,125 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'guess_app_bar.dart';
import 'result_notice.dart';
class GuessPage extends StatefulWidget {
const GuessPage({super.key});
@override
State<GuessPage> createState() => _GuessPageState();
}
class _GuessPageState extends State<GuessPage> with SingleTickerProviderStateMixin,AutomaticKeepAliveClientMixin{
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
);
}
int _value = 0;
final Random _random = Random();
bool _guessing = false;
bool? _isBig;
@override
void dispose() {
_guessCtrl.dispose();
controller.dispose();
super.dispose();
}
void _generateRandomValue() {
setState(() {
_guessing = true;
_value = _random.nextInt(100);
print(_value);
});
}
TextEditingController _guessCtrl = TextEditingController();
void _onCheck() {
print("=====Check:目标数值:$_value=====${_guessCtrl.text}============");
int? guessValue = int.tryParse(_guessCtrl.text);
// 游戏未开始,或者输入非整数,无视
if (!_guessing || guessValue == null) return;
controller.forward(from: 0);
//猜对了
if (guessValue == _value) {
setState(() {
_isBig = null;
_guessing = false;
});
return;
}
// 猜错了
setState(() {
_isBig = guessValue > _value;
});
_guessCtrl.clear();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: GuessAppBar(
controller: _guessCtrl,
onCheck: _onCheck,
),
body: Stack(
children: [
if(_isBig!=null)
Column(
children: [
if(_isBig!)
ResultNotice(color:Colors.redAccent,info:'大了',controller: controller,),
Spacer(),
if(!_isBig!)
ResultNotice(color:Colors.blueAccent,info:'小了',controller: controller,),
],
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (!_guessing)
const Text(
'点击生成随机数值',
),
Text(
_guessing ? '**' : '$_value',
style: const TextStyle(
fontSize: 68, fontWeight: FontWeight.bold),
),
],
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _guessing ? null : _generateRandomValue,
backgroundColor: _guessing ? Colors.grey : Colors.blue,
tooltip: 'Increment',
child: const Icon(Icons.generating_tokens_outlined),
),
);
}
@override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
class ResultNotice extends StatelessWidget {
final Color color;
final String info;
final AnimationController controller;
const ResultNotice({
Key? key,
required this.color,
required this.info,
required this.controller,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
child: Container(
alignment: Alignment.center,
color: color,
child: AnimatedBuilder(
animation: controller,
builder: (_, child) => Text(
info,
style: TextStyle(
fontSize: 54 * (controller.value),
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
);
}
}

View File

@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
class ColorSelector extends StatelessWidget {
final List<Color> supportColors;
final ValueChanged<int> onSelect;
final int activeIndex;
const ColorSelector({
Key? key,
required this.supportColors,
required this.activeIndex,
required this.onSelect,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8),
child: Wrap(
// crossAxisAlignment: CrossAxisAlignment.end,
children: List.generate(
supportColors.length,
_buildByIndex,
)),
);
}
Widget _buildByIndex(int index) {
bool select = index == activeIndex;
return GestureDetector(
onTap: () => onSelect(index),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 2),
padding: const EdgeInsets.all(2),
width: 24,
height: 24,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
// borderRadius: BorderRadius.circular(8),
border: select ? Border.all(color: Colors.blue) : null
),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: supportColors[index],
),
),
),
);
}
}

View File

@@ -0,0 +1,102 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
/// create by 张风捷特烈 on 2020-04-23
/// contact me by email 1981462002@qq.com
/// 说明:
class ConformDialog extends StatelessWidget {
final String title;
final String msg;
final String conformText;
final VoidCallback onConform;
ConformDialog({
Key? key,
required this.title,
required this.msg,
required this.onConform,
this.conformText = '删除',
}) : super(key: key);
final ButtonStyle conformStyle = ElevatedButton.styleFrom(
backgroundColor: Colors.redAccent,
foregroundColor: Colors.white,
elevation: 0,
minimumSize: const Size(70, 35),
padding: EdgeInsets.zero,
shape: const StadiumBorder(),
);
final ButtonStyle cancelStyle = OutlinedButton.styleFrom(
minimumSize: const Size(70, 35),
elevation: 0,
padding: EdgeInsets.zero,
shape: const StadiumBorder(),
);
@override
Widget build(BuildContext context) {
return Dialog(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_buildTitle(),
_buildMessage(),
_buildButtons(context),
],
),
),
);
}
Widget _buildTitle()=>Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.warning_amber_rounded, color: Colors.orange),
const SizedBox(
width: 10,
),
Text(
title,
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.bold),
),
],
);
Widget _buildMessage()=>Padding(
padding: const EdgeInsets.only(
top: 15,
bottom: 15,
),
child: Text(
msg,
style: const TextStyle(fontSize: 14),
),
);
Widget _buildButtons(BuildContext context) => Row(
children: [
const Spacer(),
OutlinedButton(
onPressed: Navigator.of(context).pop,
style: cancelStyle,
child: const Text(
'取消',
style: TextStyle(fontSize: 12, color: Colors.grey),
)),
const SizedBox(width: 10),
ElevatedButton(
onPressed: onConform,
style: conformStyle,
child: Text(conformText),
),
],
);
}

View File

@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
class Line {
List<Offset> points;
Color color;
double strokeWidth;
Line({
required this.points,
this.color = Colors.black,
this.strokeWidth = 1,
});
}

194
lib/cases/paper/paper.dart Normal file
View File

@@ -0,0 +1,194 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'color_selector.dart';
import 'conform_dialog.dart';
import 'model.dart';
import 'paper_app_bar.dart';
import 'stork_width_selector.dart';
class Paper extends StatefulWidget {
const Paper({Key? key}) : super(key: key);
@override
State<Paper> createState() => _PaperState();
}
class _PaperState extends State<Paper> with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
List<Line> _lines = []; // 线列表
int _activeColorIndex = 0; // 颜色激活索引
int _activeStorkWidthIndex = 0; // 线宽激活索引
// 支持的颜色
final List<Color> supportColors = [
Colors.black,
Colors.red,
Colors.orange,
Colors.yellow,
Colors.green,
Colors.blue,
Colors.indigo,
Colors.purple,
Colors.pink,
Colors.grey,
Colors.redAccent,
Colors.orangeAccent,
Colors.yellowAccent,
Colors.greenAccent,
Colors.blueAccent,
Colors.indigoAccent,
Colors.purpleAccent,
Colors.pinkAccent,
];
// 支持的线粗
final List<double> supportStorkWidths = [1, 2, 4, 6, 8, 10];
List<Line> _historyLines = [];
void _back() {
Line line = _lines.removeLast();
_historyLines.add(line);
setState(() {});
}
void _revocation() {
Line line = _historyLines.removeLast();
_lines.add(line);
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PaperAppBar(
onClear: _showClearDialog,
onBack: _lines.isEmpty ? null : _back,
onRevocation: _historyLines.isEmpty ? null : _revocation,
),
body: Stack(
children: [
GestureDetector(
onPanStart: _onPanStart,
onPanUpdate: _onPanUpdate,
child: CustomPaint(
painter: PaperPainter(lines: _lines),
child: ConstrainedBox(constraints: const BoxConstraints.expand()),
),
),
Positioned(
bottom: 0,
width: MediaQuery.of(context).size.width,
child: Row(
children: [
Expanded(
child: ColorSelector(
supportColors: supportColors,
activeIndex: _activeColorIndex,
onSelect: _onSelectColor,
),
),
StorkWidthSelector(
supportStorkWidths: supportStorkWidths,
color: supportColors[_activeColorIndex],
activeIndex: _activeStorkWidthIndex,
onSelect: _onSelectStorkWidth,
),
],
),
),
],
),
);
}
void _showClearDialog() {
String msg = "您的当前操作会清空绘制内容,是否确定删除!";
showDialog(
context: context,
builder: (ctx) => ConformDialog(
title: '清空提示',
conformText: '确定',
msg: msg,
onConform: _clear,
));
}
void _clear() {
_lines.clear();
Navigator.of(context).pop();
setState(() {});
}
void _onPanStart(DragStartDetails details) {
_lines.add(Line(
points: [details.localPosition],
strokeWidth: supportStorkWidths[_activeStorkWidthIndex],
color: supportColors[_activeColorIndex],
));
}
void _onPanUpdate(DragUpdateDetails details) {
Offset point = details.localPosition;
double distance = (_lines.last.points.last - point).distance;
if (distance > 5) {
_lines.last.points.add(details.localPosition);
setState(() {});
}
}
void _onSelectStorkWidth(int index) {
if (index != _activeStorkWidthIndex) {
setState(() {
_activeStorkWidthIndex = index;
});
}
}
void _onSelectColor(int index) {
if (index != _activeColorIndex) {
setState(() {
_activeColorIndex = index;
});
}
}
}
class PaperPainter extends CustomPainter {
PaperPainter({
required this.lines,
}) {
_paint = Paint()
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
}
late Paint _paint;
final List<Line> lines;
@override
void paint(Canvas canvas, Size size) {
for (int i = 0; i < lines.length; i++) {
drawLine(canvas, lines[i]);
}
}
///根据点位绘制线
void drawLine(Canvas canvas, Line line) {
_paint.color = line.color;
_paint.strokeWidth = line.strokeWidth;
canvas.drawPoints(PointMode.polygon, line.points, _paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}

View File

@@ -0,0 +1,76 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class PaperAppBar extends StatelessWidget implements PreferredSizeWidget {
final VoidCallback onClear;
final VoidCallback? onBack;
final VoidCallback? onRevocation;
const PaperAppBar({
Key? key,
required this.onClear,
this.onBack,
this.onRevocation,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return AppBar(
leading: BackUpButtons(
onBack: onBack,
onRevocation: onRevocation,
),
leadingWidth: 100,
title: Text('画板绘制'),
actions: [
IconButton(
splashRadius: 20,
onPressed: onClear,
icon: Icon(CupertinoIcons.delete, size: 20))
],
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}
class BackUpButtons extends StatelessWidget {
final VoidCallback? onBack;
final VoidCallback? onRevocation;
const BackUpButtons({
Key? key,
required this.onBack,
required this.onRevocation,
}) : super(key: key);
@override
Widget build(BuildContext context) {
const BoxConstraints cts = BoxConstraints(minHeight: 32, minWidth: 32);
Color backColor = onBack ==null?Colors.grey:Colors.black;
Color revocationColor = onRevocation ==null?Colors.grey:Colors.black;
return Center(
child: Wrap(
children: [
Transform.scale(
scaleX: -1,
child: IconButton(
splashRadius: 20,
constraints: cts,
onPressed: onBack,
icon: Icon(Icons.next_plan_outlined,color: backColor),
),
),
IconButton(
splashRadius: 20,
onPressed: onRevocation,
constraints: cts,
icon: Icon(Icons.next_plan_outlined, color: revocationColor),
)
],
),
);
}
}

View File

@@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
class StorkWidthSelector extends StatelessWidget {
final List<double> supportStorkWidths;
final ValueChanged<int> onSelect;
final int activeIndex;
final Color color;
const StorkWidthSelector({
Key? key,
required this.supportStorkWidths,
required this.activeIndex,
required this.onSelect,
required this.color,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: List.generate(
supportStorkWidths.length,
_buildByIndex,
)),
);
}
Widget _buildByIndex(int index) {
bool select = index == activeIndex;
return GestureDetector(
onTap: () => onSelect(index),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 2),
width: 70,
height: 18,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: select ? Border.all(color: Colors.blue) : null),
child: Container(
width: 50,
color: color,
height: supportStorkWidths[index],
),
),
);
}
}