Я собираюсь предсказать это, сказав: «Пожалуйста, не принимайте ничего, что я скажу в этом ответе лично - мне известны усилия be nice
, которые StackOverflow пытается реализовать. Но я также скажу следующее: вам нужно , чтобы разобраться в очистке своего кода. Это не личная атака, просто несколько слов от человека, который хочет помочь вам стать программистом.
Вы определенно должны разбивать логику на более мелкие куски - общее правило состоит в том, что если ваша функция сборки занимает больше, чем страница по вертикали, она, вероятно, делает слишком много. Это верно как для вас, как для программиста, так и для производительности приложения, поскольку флаттер использует механизм восстановления на основе состояния, на который влияют большие функции сборки.
Другая пара подсказок:
Не используйте GlobalKey, если это вообще возможно. Ваш AppBar должен быть инкапсулирован в свой собственный виджет или, по крайней мере, построен с помощью Builder , чтобы он получал контекст, включающий скаффолд. Тогда вы можете использовать Scaffold.of(....)
вместо глобального ключа.
Если вы собираетесь использовать статическую переменную, такую как static var change = [1, 2, 0]
, вам, вероятно, будет лучше использовать enum - особенно без комментариев к документации, которые ничего не значат для кого-то, кто смотрит на ваш код (т.е. я = D). Использование enum сделает ваш код более читабельным. Также, пожалуйста, подумайте о том, чтобы называть вещи по-другому - cchanged
ничего не значит для кого-либо, кроме вас (и, возможно, даже для вас, если вы отойдете на пару недель).
Список цветов в вашем художнике и виджете является ошибкой кодирования, ожидающей своего появления. Построение объектов Paint дешево, если вы не делаете это в цикле - просто создайте новый экземпляр в функции сборки вместо сохранения списка и передайте цвет напрямую! Или, по крайней мере, создайте enum
под названием Палитра или что-то в этом роде и используйте это для принятия решений, а не для списка - по крайней мере, если вы используете переключатель, анализатор дротиков поможет вам удовлетворить все возможные варианты. .
Это повторение второго абзаца, но достаточно важно повторить ... разбить ваш класс на несколько виджетов с сохранением состояния или без состояния ... или, по крайней мере, разбить вашу функцию сборки на несколько функций , Это сделает вещи менее сложными, более понятными и более производительными. Пример того, почему каждый раз, когда вы рисуете одну точку на холсте, вы, вероятно, перерисовываете строку заголовка, нижнюю строку и все остальное, что вы видите на экране одновременно, просто потому, что все это в одном виджете! Ваш statefulwidget должен создавать только визуальную часть с состоянием!
При использовании setState
фактически установить состояние в функции setState
. Прямо сейчас , все будет работать, если вы этого не сделаете ... но это не так семантически ясно, что вы делаете, и это может очень легко сломаться в будущем, если разработчики флаттера сделают изменение в том, как setState работы.
Поскольку вы используете Center
в своих закусочных, они расширяются и занимают весь экран. Я совершенно уверен, что вы этого не хотите, поэтому добавьте heightFactor = 1.0
, чтобы они не расширялись по вертикали. Я оставлю вам решать, хотите ли вы продолжать использовать этот визуальный механизм для изменения цвета, который вы меняете - лично я думаю, что это не очень хорошо, поскольку он покрывает область, которую вы фактически используете для выбора цвета. .
Я собираюсь догадаться, что это просто быстрое приложение, созданное для развлечения, и что вы находитесь в процессе изучения флаттера и / или кодирования. Но возьмите его у профессионального разработчика и временного менеджера по найму - потрате лишнюю пару минут, чтобы убедиться, что ваш код читабелен и прост для понимания сэкономит ваше время в долгосрочной перспективе и что чистое написание код даже в одноразовых проектах поможет вам развить хорошие привычки, которые помогут вам стать лучшим программистом . Кроме того - когда я проверяю возможную аренду, если я вижу действительно грязный код в одном из их общедоступных репозиториев github, это еще одна причина не давать им интервью. Я знаю, что не все, что вы делаете на github, предназначено для широкой публики, но чистота кода - это простой навык, который нужно развивать, он не занимает много времени и много говорит о вас как о программисте!
И последнее: требовать, чтобы на ваш вопрос ответили определенным образом, немного грубо, учитывая, что мы на stackoverflow делаем это бесплатно, потому что хотим помочь сообществу. Я смягчил тон в вашем вопросе, но имейте это в виду, потому что вежливость ==> лучшие ответы!
Теперь перейдем к вашей актуальной проблеме - все сводится к следующему утверждению:
@override
bool shouldRepaint(MyPainter oldDelegate) {
return oldDelegate.canvasp.length != canvasp.length;
}
В дартсе (и многих других языках программирования, но не во всех) сравнение списка с! = Или == делает только то, что я бы назвал «мелким сравнением»; то есть только проверяет, является ли список тем же списком . Поскольку вы добавляете новую точку в список , тот же самый каждый раз, когда вы обнаруживаете жест, но не создаете заново список , исходящий оператор всегда возвращает false, и поэтому ваш холст никогда не перезаписывается. -draws.
Самый простой способ решить эту проблему - копировать список в новый список каждый раз, когда вы добавляете что-то - таким образом списки будут сравниваться как разные. Однако это не очень хороший ответ, если вы имеете дело со множеством элементов, которые, скорее всего, будут здесь.
Вместо этого я предлагаю использовать счетчик, который вы используете для отслеживания каждого изменения списка. Я внес изменение в некоторый код ниже - я называю это _revision
. Каждый раз, когда вы добавляете в список, также увеличиваете _revision
и передаете его на холст. На вашем холсте вы просто должны проверить, является ли ревизия такой же. Я бы предложил создать метод, который будет выполнять инкремент для вас, а также убедиться, что setState вызывается надлежащим образом.
import 'package:flutter/material.dart';
main() {
runApp(
new MaterialApp(
title: 'Flutter Database Test',
theme: ThemeData(
textTheme: TextTheme(
display1: TextStyle(fontSize: 24.0, color: Colors.white),
display2: TextStyle(fontSize: 24.0, color: Colors.grey),
display3: TextStyle(fontSize: 18.0, color: Colors.black),
),
),
home: new MyHomePage(),
),
);
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Color> color = [
Colors.white,
Colors.black,
Colors.pink,
Colors.blue,
Colors.red,
Colors.yellow,
Colors.orange,
Colors.green,
Colors.cyan,
Colors.purple,
Colors.brown,
Colors.indigo,
Colors.teal,
Colors.grey,
];
Color _pencolor = Colors.white;
Color _canvasclr = Colors.black;
bool ispen = true;
Color bc = Colors.black54;
List<Offset> points = List<Offset>();
GlobalKey<ScaffoldState> _skey = GlobalKey<ScaffoldState>();
int cchanged = change[1];
int _revision = 0;
static var change = [1, 2, 0];
@override
Widget build(BuildContext context) {
MyPainter _painter =
MyPainter(color: color.indexOf(_pencolor), canvasp: points, changed: cchanged, revision: _revision);
_painter.addListener(() {
print('hello');
});
return new Scaffold(
resizeToAvoidBottomPadding: true,
key: _skey,
appBar: AppBar(
backgroundColor: bc,
elevation: 0.0,
title: Text(
'Draw',
style: Theme.of(context).textTheme.display1,
),
centerTitle: true,
actions: <Widget>[
IconButton(
icon: Icon(Icons.content_paste),
onPressed: !ispen
? null
: () {
_skey.currentState.showSnackBar(
SnackBar(
backgroundColor: Colors.transparent,
content: Center(
heightFactor: 1.0,
child: Text(
'Choose Canvas Color',
textScaleFactor: 0.7,
style: Theme.of(context).textTheme.display3,
),
),
),
);
setState(() {
ispen = false;
});
},
),
IconButton(
icon: Icon(Icons.edit),
onPressed: ispen
? null
: () {
_skey.currentState.showSnackBar(
SnackBar(
content: Center(
heightFactor: 1.0,
child: Text('Choose Pen Color',
textScaleFactor: 0.7, style: Theme.of(context).textTheme.display3)),
backgroundColor: Colors.transparent,
),
);
setState(() {
ispen = true;
});
},
)
],
),
drawer: Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text('Create Canvas'),
leading: Icon(Icons.add),
onTap: () {
//TODO
},
),
ListTile(
title: Text('Connect to Canvas'),
leading: Icon(Icons.compare_arrows),
onTap: () {
//TODO
},
)
],
),
),
body: Container(
color: bc,
child: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 10.0),
child: ClipRRect(
child: Container(
color: _canvasclr,
child: GestureDetector(
onPanStart: (DragStartDetails d) {
setState(() {
points.add(Offset(d.globalPosition.dx, d.globalPosition.dy - 100));
cchanged = change[2];
_revision++;
});
print('${d.globalPosition},$points');
},
onPanUpdate: (DragUpdateDetails d) {
print('${d.globalPosition}');
setState(() {
points.add(Offset(d.globalPosition.dx, d.globalPosition.dy - 100));
cchanged = change[0];
_revision++;
});
},
child: CustomPaint(
isComplex: true,
willChange: false,
child: Container(),
painter: _painter,
),
),
),
borderRadius: BorderRadius.circular(25.0),
),
),
),
Container(
height: 75.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: color.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
splashColor: Colors.white,
onTap: () {
setState(() {
ispen ? (_pencolor = color[index]) : (_canvasclr = color[index]);
});
},
child: Padding(
padding: const EdgeInsets.all(12.0),
child: CircleAvatar(
backgroundColor: color[index],
),
),
);
},
),
),
],
),
),
);
}
}
class MyPainter extends CustomPainter {
final int color;
final List<Offset> canvasp;
final int revision;
Paint p = Paint();
List<Paint> _paint = [
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.white,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.black,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.pink,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.blue,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.red,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.yellow,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.orange,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.green,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.cyan,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.purple,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.brown,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.indigo,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.teal,
Paint()
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..strokeWidth = 2.0
..color = Colors.grey,
];
final int changed;
MyPainter({this.color, this.canvasp, this.changed, this.revision});
@override
void paint(Canvas canvas, Size size) {
print('painting ....... $canvasp');
for (int i = 0; i < canvasp.length; i++) {
canvas.drawCircle(canvasp[i], 10.0, _paint[color]);
}
}
@override
bool shouldRepaint(MyPainter oldDelegate) {
return oldDelegate.revision != revision;
}
}
Я добавил в параметр ревизии и сделал несколько других небольших исправлений. Ваш код все еще нуждается в значительном рефакторинге, но это должно по крайней мере заставить его работать. И, пожалуйста, помните мои другие комментарии =).
Также - взгляните на этот вопрос и ответ , так как он делает что-то похожее и может помочь с сохранением полученного чертежа, если это то, что вы хотите сделать в конце концов!