GithubHelp home page GithubHelp logo

todo_list's Introduction

Todo List

Todo List

Простое приложение для ведения списка задач, созданное в рамка Школы мобильной разработки яндекса

ВНИМАНИЕ

Просмотр информации о задаче работает, для этого нужно нажать на название задачи, а не на кнопку информации справа, исправить это можно одной строчкой, но тк я забыл, а комитить больше нельзя 😥😥😥, пишу это предупреждение

Demo

https://rn7cvj.github.io/todo_list/ — web версия

animation_preview

Download

Последнюю версию для Android можно найти здесь Github Releases.

Features

  • Поддержка локлизации для русского и английского языка

    Подробнее:
    Ru En
    localization_ru localization_en
  • Тёмная/Светлая тема.

    Превью:
    Dark Light
    task_list_dark task_list_ligth
  • Анимированное добавление, удаление задач и заголовка.

todo_list's People

Contributors

rn7cvj avatar

Watchers

 avatar

todo_list's Issues

Проверка ДЗ 2

Привет, проверяю твое дз, постараюсь максимально подробно изложить все замечания и подкрепить их документацией.

1. flutter_lints.

Я вижу, что у тебя в зависимостях подтягивается flutter_lints. По умолчанию он настроен уже в любом проекте, но ты можешь переопределять правила его работы. Даже, если ты их не переопределял, то он все равно тебе выдает подсказки, но они у тебя были проигнорированы. Смотри ниже:

  • lowerCamelCase
    Переменным не дается название с нижним подчеркиванием. Должны называться в таком формате [lowerCamelCase]

    DateTime created_at,
    DateTime changed_at,
    String last_updated_by, {

    Подробнее

  • prefer_const_constructors
    В некоторых местах можно явно указать, что виджет будет константным. Да, иногда не всегда это нужно делать. Но в данном случае думаю нужно было оставить const.

    duration: Duration(milliseconds: 300),

    duration: const Duration(milliseconds: 300),
    Подробнее: Dart Documentation

  • prefer_const_declarations
    Используй const, вместо final, где это возможно. Константные конструкции определяются во время компиляции, а финальные при выполнении.
    Подробнее: Dart Documentation
    Подробнее: Metanit

2. Форматирование кода.

В некоторых местах есть выходы кода за ограничение. Возможно ты делал autoformat, но это не помогало.

  • Запятая после переменной

const Padding(
padding: EdgeInsets.only(top: appPaddingMedium, bottom: appPaddingMedium),
child: Divider(
height: 2,
),
),

Если поставим , после bottom: appPaddingMedium, при автоформатировании это учтется и получится что-то такое:

 const Padding(
   padding: EdgeInsets.only(
      top: appPaddingMedium,
      bottom: appPaddingMedium,
   ),
   child: Divider(
      height: 2,
   ),
 ),

Вся информация про линтер и форматирование тут -> Dart Documentation

Все остальное реализовано вроде прилично(Учитывая, что я не последователь GetIT и MobX)

Придирки

Тут я опишу некоторые моменты, которы по моему мнению кажутся важными. (Можно игнорировать)

  • Пакеты для маленьких задач.
    Я бы не стал тянуть лишние пакеты, чтобы вызывать у них конкретную функциональность. С одной стороны я посмотрел и обновление пакета было недавно, но с другой стороны поддержка может прекратиться в любой момент.

    restart_app: ^1.2.1

  • Использование определенных хранилищ данных для определенных целей.
    Я бы не использовал get_storage, так как он предназначен для хранения каких-то маленьких данных, которые не нужно индексировать. В принципе они сами об это пишут в документации (в самом низу).
    Иначе у тебя получаются вот такие ситуации, как ниже:

Тебе приходится перебирать все ключи и запоминать их значения, а потом по ним делать еще и запросы на получение значения. Теперь представь, что у тебя так миллион записей :).

Future<List<Map<String, dynamic>>?> getAllTasks() async {
List<String> keys = _box?.getKeys().toList();
List<Map<String, dynamic>>? values;
if (keys.isNotEmpty) {
values = [];
}
for (String key in keys) {
values?.add(_box?.read(key));
}
return values;
}

Если бы это был, к примеру, sqlflie(SQL), то там бы вопрос решался одной строчкой SELECT * from tasks where uuid == ...

  • Виджет тяжело переиспользовать.

У тебя есть виджет

class DeleteTaskButton extends StatelessWidget {
DeleteTaskButton({super.key, required this.taskUid});
final String taskUid;
final TaskListContoller contoller = GetIt.I<TaskListContoller>();

И в самом низу ты определяешь для него логику нажатия

Future<void> deleteTask() async {
runInAction(() => isSyncing.value = true);
await contoller.deleteTask(taskUid);
navigationManager.popToHomePage();
}

Выходит так, что это виджет у тебя может делать только одно действие. А теперь представь ситуацию, когда тебе нужно точно такой же виджет показывать на другом экране, но с другой например кнопкой и логикой. Ты либо будешь новый виджет писать(копировать), либо можешь переделать этот так, что в качестве параметра он принимал метод Function() deleteTask

И таких виджетов, как я понял, у тебя несколько.

Как-то так)

Итого.

  • Чистота кода и общая структура проекта (4б)
    форматирование: 0.5
    линт: 0.5
  • State-Management (8)
  • Получение и хранение данных (10)
  • Разное (3)
    Итого: 25

Если с чем-то не согласен - пиши, баллы еще не ставил

Проверка ДЗ1

Привет! Я проверил твой код по первому заданию.
Гладко стелишь, что сказать) А если серьёзно, то мне есть чему у тебя поучиться в плане подхода к некоторым задачам.

Наверное самое близкое, к чему можно придраться, так это дизайн, который не соответствует заданной в макете цветовой палитре. Дефолтный material 3, конечно, смотрится хорошо, но есть некоторые прям явные проблемы. Например в белой теме на экранах редактирования/создания задачи у тебя поле ввода фиолетовое и тут сложно найти любителя, которому такое бы понравилось)
Я бы посоветовал выделить темы, шрифты и цветовые палитры в отдельные файлы. Цвета для палитр можно взять тут:
https://m3.material.io/theme-builder#/custom
Если установить сидом синий цвет из макета, переопределить ещё несколько цветов и поставить прозрачным surfaceTint, то должно получиться куда ближе к макету и при этом красиво.
По сути это к вёрстке относится, но баллы снять за такое у меня рука не поднимается.

Про кнопку инфо ты и сам написал, это, конечно, лучше пофиксить.

Также я бы посоветовал делать хотя бы какую-то базовую валидацию на установку задач, потому что сейчас можно пустую задачу сохранить без описания.

Но есть и относительно существенный косяк с состоянием задачи, когда работает скрытие выполненных. Дело в том, что если включить скрытие и попытаться отметить задачу свайпом, то у тебя метод toggle вызывается дважды и задача на экране воскреснет не отмеченной. Тут всё же придётся снять балл про критерию, потому что баг ломает функцию.

Таким образом расценка:
Чистота кода и общая структура проекта: 4/4
Вёрстка + навигация: 15/16 (минус балл в категории "свайп по айтему Выполнено/Удалить с логикой")
Логирование: 2/2
Иконка: пока 0/4
По истории коммитов увидел, что ты использовал flutter_launcher_icons, а потом в следующем коммите его из зависимостей удалил. Возможно я что-то проглядел и ты потом всё же сам иконку установил. Скажи пожалуйста, если так, с указанием конкретного коммита, где ты это сделал.

А пока оценка 21/26.
Приятно было почитать твой код, многому научился, за это спасибо. Но поработай немного над темами ещё.
И удачи тебе в последующих домашках)

дз3

Чистота кода и общая структура проекта:

  • Есть up-to-date README со ссылкой на загрузку .apk, перечнем реализованных фич, пара скриншотов 1
  • Добавлен и работает flutter_lints, в коде нет необоснованных игноров правил 0.5
  • Форматирование кода присутствует 1
  • Код разбит на фичи и слои 2

Получение и хранение данных

  • Реализована обработка серверных и прочих ошибок там, где они могут часто стрелять 2
  • Приложение адекватно работает без интернета 2
  • Offline-first 2

Architecture

  • Работа с данными организована в отдельной сущности, репозитории, инкапсулируя в одном месте получение и обновление/добавление данных на диск 3
  • State-management реализован с использованием одного из предложенных фреймворков 2
  • Есть DI 2

Testing

  • Unit-тестами покрыты сущности с бизнес-логикой, тест осмысленный и полезный 2
  • Реализован интеграционный тест 0

Deeplinks

  • Поддержан диплинк, открывающий экран добавления нового дела 0.5
    не работает без:
    image
  • Диплинк открывается даже если приложение было убито 1
  • Слой навигации переписан на Navigator 2.0 1

22

Проверка ДЗ 1

Привет, я проверяю твое домашнее задание.

Я смотрю на твой реализованный аппбар, и у меня возникают вопросы.
Я наблюдаю некий скачок текста при переходе в коллапс аппбара, это ожидаемо?
Явно отсутствует иконка скрытия/показа выполненных заданий в коллапс аппбаре.

Классно, что ты самостоятельно вычисляешь прогресс, для сворачивания и разворачивания аппбара. Мне кажется, что ты вполне можешь улучшить свой аппбар внимательно по исследовав виджет FlexibleSpaceBar, там можно найти нечто интересное)

К сожалению, замечаю, что в тексте присутствуют орфографические ошибки в названиях переменных/методов.

builder: (context) => buildTaskList(context, contoller.getTasks),

Widget buildTaskList(BuildContext context, List<Task> tasks) {
List<Widget> tiles = [];
//Добавляем один виджет со скруглениями сверху
if (tasks.isNotEmpty) {
// ignore: unnecessary_cast
tiles.add(TaskTile(task: tasks.first, isFirst: true) as Widget);
}
if (tasks.length > 1) {
// Тут нужен этот каст иначе не получается добавить кнопку новое которая внизу
// ignore: unnecessary_cast
tiles.addAll(tasks.sublist(1).map((task) => TaskTile(
task: task,
isFirst: false,
) as Widget));
}
if (tasks.isNotEmpty) {
tiles.add(const Divider(
indent: appPaddingMedium,
endIndent: appPaddingMedium,
height: 0,
));
}
//Согласно заветам тз "Анимации не ожидаем."
return AnimatedSize(
alignment: Alignment.topCenter,
duration: animationDurationFast,
child: Column(
children: tiles,
),
);
}

Не очень понимаю, зачем тут нужен был отдельный метод, да еще и публичный, когда можно было сделать

Widget buildTaskList(BuildContext context, List<Task> tasks) {
    //Согласно заветам тз "Анимации не ожидаем."
    return AnimatedSize(
      alignment: Alignment.topCenter,
      duration: animationDurationFast,
      child: Column(
        children: [
          if (tasks.isNotEmpty) TaskTile(task: tasks.first, isFirst: true),
          if (tasks.length > 1)
            ...tasks.map((task) => TaskTile(task: task, isFirst: false)),
          if (tasks.isNotEmpty)
            const Divider(
              indent: appPaddingMedium,
              endIndent: appPaddingMedium,
              height: 0,
            )
        ],
      ),
    );
  }

А еще бы лучше, вынести в отдельный виджет, как и ПУБЛИЧНЫЙ метод buildAddNewTaskButton

TaskTile({super.key, required this.task, required this.isFirst});

можно было запятую указать, чтоб при форматировании, все читалось проще.

К сожалению, я вижу, что это публичные методы и виджеты, которые собираются в методах, да еще и могут лежать в переменных, после мапа, только утяжеляет виджет, понимаю, что всё, что относится к указанному экрану разбиты на методы, но это явно же проигрывает, когда мы их выносим в виджеты. Как минимум, они все будут перерисовываться.

Принты:

print('invalid initialRoutes ($initialRoutes)');

print('generated empty initial routes ($initialRoutes)');

Итог:

  • Чистота кода и общая структура проекта - 4 балла
  • Верстка
    -- Полностью свёрстаны 2 экрана по макетам - 10 балла
    -- Свайп - 1.5 балла
    -- Показ выполненных дел с логикой - 1.5 балла (нельзя показать выполненные дела, если задач > n, так чтоб аппбар можно было свернуть)
  • Разное
    -- Реализовано нормальное логирование (не print'ы) - 1 балл (нашел принты в коде)
    -- Есть человеческая иконка у приложения (iOS или Android) - 4 балла

22 балла

Апп бар находится в суперпозиция

Если немного проскролить вниз при показанных задачах, а потом кликнуть на кнопку скрытия, то он скроет задачи, а аппбар зависнет в том состоянии, в котором он был

Screenshot_20230622_090220

Не выполняется задание

Присутствует бага, когда задача два раза вызывает _$TaskListContoller.toogleTaksComplitedStatus

device-2023-06-22-085437.mp4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.