Рисуем приложение во Flutter.Stuck с перекрашиванием - PullRequest
0 голосов
/ 04 июля 2018

Это файл main.dart моего приложения:

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];

  static var change = [1, 2, 0];

  @override
  Widget build(BuildContext context) {
    MyPainter _painter = MyPainter(color: cchanged, canvasp: points, changed: color.indexOf(_pencolor));
    _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: () {
                _skey.currentState.showSnackBar(SnackBar(
                    backgroundColor: Colors.transparent,
                    content: Center(
                        child: Text(
                      'Choose Canvas Color',
                      textScaleFactor: 0.7,
                      style: Theme.of(context).textTheme.display3,
                    ))));
                ispen = false;
              }),
          IconButton(
              icon: Icon(Icons.edit),
              onPressed: () {
                _skey.currentState.showSnackBar(SnackBar(
                  content: Center(
                      child:
                          Text('Choose Pen Color', textScaleFactor: 0.7, style: Theme.of(context).textTheme.display3)),
                  backgroundColor: Colors.transparent,
                ));
                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) {
                        points.add(Offset(d.globalPosition.dx, d.globalPosition.dy - 100));
                        cchanged = change[2];
                        print('${d.globalPosition},$points');
                        setState(() {});
                      },
                      onPanUpdate: (DragUpdateDetails d) {
                        points.add(Offset(d.globalPosition.dx, d.globalPosition.dy - 100));
                        print('${d.globalPosition}');
                        cchanged = change[0];
                        setState(() {});
                      },
                      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: () {
                      ispen ? _pencolor = color[index] : _canvasclr = color[index];
                      setState(() {});
                    },
                    child: Padding(
                      padding: const EdgeInsets.all(12.0),
                      child: CircleAvatar(
                        backgroundColor: color[index],
                      ),
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class MyPainter extends CustomPainter {
  final int color;
  final List<Offset> canvasp;
  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,
  });

  @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.canvasp.length != canvasp.length;
  }
}

В этой реализации показано приложение, которое я пытаюсь построить для рисования на холсте.

Однако я не могу заставить его рисовать, когда касаюсь экрана. Вместо этого он перерисовывается только тогда, когда я меняю цвет в чернильнице - но не когда он пишет или когда события получаются в GestureDetector.

Буду признателен за ответ в виде изменения моего существующего кода.

1 Ответ

0 голосов
/ 05 июля 2018

Я собираюсь предсказать это, сказав: «Пожалуйста, не принимайте ничего, что я скажу в этом ответе лично - мне известны усилия 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;
  }
}

Я добавил в параметр ревизии и сделал несколько других небольших исправлений. Ваш код все еще нуждается в значительном рефакторинге, но это должно по крайней мере заставить его работать. И, пожалуйста, помните мои другие комментарии =).

Также - взгляните на этот вопрос и ответ , так как он делает что-то похожее и может помочь с сохранением полученного чертежа, если это то, что вы хотите сделать в конце концов!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...