FlutterGO: мастер-класс по Fullstack.
Начните работать с двумя наиболее популярными технологиями Google!
Если вы читаете это, значит, вам либо слишком скучно и нечем заняться, либо вам просто надоели все хипстерские штучки, которые вы изучали раньше, и теперь вы хотите поднять уровень своей игры в программную инженерию и изучить либо Flutter, либо GO, а может быть, и то, и другое. Как бы там ни было, я вас понял.
Что такое Flutter?
Flutter, разработанный техническими мастерами Google, представляет собой набор средств разработки пользовательского интерфейса (SDK) с открытым исходным кодом, позволяющий создавать нативно скомпилированные приложения для нескольких платформ на основе единой кодовой базы. Это означает, что вы можете создавать потрясающие приложения для iOS, Android, веб и даже настольных компьютеров на основе единой кодовой базы – вот что значит эффективность!
Что такое GO?
Язык Go, также известный как Golang, был создан группой блестящих инженеров компании Google. Это язык программирования с открытым исходным кодом, который сочетает в себе простоту разработки динамических языков, таких как Python, с производительностью и безопасностью статически-типизированных языков, таких как C++. Это уникальное сочетание делает Go фантастическим вариантом для создания надежного и масштабируемого программного обеспечения.
Итак… Что же такое FlutterGO?
Вы угадали! Flutter для клиентской части, GO для серверной!
Создание API в GO
Мы создадим API в GO с помощью gorilla mux. Не волнуйтесь, это всего лишь пакет, который поможет нам в создании API.
Перед написанием кода загрузите пакет Mux, перейдите в корень вашего проекта go и введите;
go get -u github.com/gorilla/mux
Или можно посетить страницу установки.
Начнем с операторов импорта.
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
Если вы не понимаете всех этих импортов, не беспокойтесь, они будут описаны в последующих разделах.
Структуры
Итак, мы создадим API, который сможет получать курсы, добавлять новые курсы, обновлять курсы и удалять их – в общем, старый добрый CRUD. Начнем со структур.
type Course struct {
Name string `json:"name"`
Price int `json:"price"`
Id string `json:"id"`
Author *Author `json:"author"`
}
type Author struct {
Name string `json:"name"`
Github string `json:"github"`
}
// A fake database
var courseDB []Course
Пока мы не будем использовать базу данных, пример использования MongoDB Atlas с GO можно посмотреть по этой ссылке. В качестве источника данных будет выступать срез courseDB.
Для работы наших конечных точек API мы сделаем следующие методы.
func courses(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
getCourses(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func course(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
getCourse(w, r)
case "POST":
addCourse(w, r)
case "PUT":
updateCourse(w, r)
case "DELETE":
deleteCourse(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
Писатель ответа, как следует из названия, используется для записи ответа, вы можете использовать функцию WriteHeader() для записи кода состояния в ответ. Request помогает нам обрабатывать запросы, подробнее об этом рассказано в первом примере.
Создание методов, использованных ранее
Теперь приступим к созданию методов, использованных ранее.
Получить все курсы
func getCourses(w http.ResponseWriter, r *http.Request) {
if len(courseDB) == 0 {
w.WriteHeader(http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
e := json.NewEncoder(w)
e.Encode(courseDB)
}
Поиск конкретного курса
func getCourse(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
e := json.NewEncoder(w)
for _, c := range courseDB {
if c.Id == params["id"] {
e.Encode(c)
return
}
}
w.WriteHeader(http.StatusNotFound)
}
Добавление курса
func addCourse(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
e := json.NewEncoder(w)
var c Course
err := json.NewDecoder(r.Body).Decode(&c)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
if c.IsEmpty() {
w.WriteHeader(http.StatusBadRequest)
return
}
courseDB = append(courseDB, c)
w.WriteHeader(http.StatusCreated)
e.Encode(c)
}
Обновление курса
func updateCourse(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
e := json.NewEncoder(w)
var crs Course
err := json.NewDecoder(r.Body).Decode(&crs)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
if crs.IsEmpty() {
w.WriteHeader(http.StatusBadRequest)
return
}
params := mux.Vars(r)
for i, c := range courseDB {
if c.Id == params["id"] {
courseDB = append(courseDB[:i], courseDB[i+1:]...)
courseDB = append(courseDB, crs)
w.WriteHeader(http.StatusCreated)
e.Encode(crs)
return
}
}
w.WriteHeader(http.StatusNotFound)
}
Удаление курса
func deleteCourse(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
for i, c := range courseDB {
if c.Id == params["id"] {
courseDB = append(courseDB[:i], courseDB[i+1:]...)
w.WriteHeader(http.StatusNoContent)
return
}
}
w.WriteHeader(http.StatusNotFound)
}
Это было очень много.
Мы можем получить полезную нагрузку с помощью mux.Vars(request), в настоящее время мы отправляем только “id” в полезной нагрузке.
Используя ResponseWriter, мы создадим наш кодировщик и закодируем ответ, который будет получен на стороне клиента.
Клиентская часть
Клиентская часть будет построена с использованием flutter. Я не буду вдаваться в детали фронтенда, уверен, что вы сможете разобраться с этим самостоятельно. Мы разберем, как использовать конечные точки API, которые мы создали во flutter.
Пакеты
Вы можете добавить свои любимые пакеты, которые я буду использовать для работы с конечными точками API. Вы можете посмотреть страницу pub dev здесь.
Вместо того чтобы читать всю документацию, перейдите в корень вашего проекта flutter и просто выполните команду;
flutter pub add http
После добавления пакета мы можем получить данные в формате json следующим образом.
Выборка всех курсов
static Future<List<Map<String, dynamic>>> fetchAllCourses() async {
const url = 'http://localhost:8080/courses';
try {
final raw = await _client.get(Uri.parse(url));
final response = jsonDecode(raw.body);
List<Map<String, dynamic>> data = [];
for (var item in response) {
data.add(item as Map<String, dynamic>);
}
return data;
} catch (e) {
debugPrint("Error: $e");
throw Exception('Failed to load courses.');
}
}
Получение конкретного курса
static Future<Map<String, dynamic>> fetchCourseById(String id) async {
final url = 'http:localhost:8080/courses/$id';
try {
final response = await http.get(Uri.parse(url));
return response.body as Map<String, dynamic>;
} catch (e) {
debugPrint("Error: $e");
throw Exception('Failed to load courses.');
}
}
Добавление курса
static Future<void> addCourse(Map<String, dynamic> course) async {
const url = 'http:localhost:8080/courses';
try {
final response = await http.post(Uri.parse(url), body: course);
if (response.statusCode != 200) {
throw Exception('Failed to add course.');
}
} catch (e) {
debugPrint("Error: $e");
throw Exception('Failed to add courses.');
}
}
Обновление курса
static Future<void> updateCourse(Map<String, dynamic> course) async {
final url = 'http:localhost:8080/courses/${course['id']}';
try {
final response = await http.put(Uri.parse(url), body: course);
if (response.statusCode != 200) {
throw Exception('Failed to update course.');
}
} catch (e) {
debugPrint("Error: $e");
throw Exception('Failed to update courses.');
}
}
Удаление курса
static Future<void> deleteCourse(String id) async {
final url = 'http:localhost:8080/courses/$id';
try {
final response = await http.delete(Uri.parse(url));
if (response.statusCode != 200) {
throw Exception('Failed to delete course.');
}
} catch (e) {
debugPrint("Error: $e");
throw Exception('Failed to delete courses.');
}
}
Обращаясь к созданным ранее конечным точкам, мы можем добиться желаемых действий! Эти специфические ответы могут быть использованы в пользовательском интерфейсе!
Как протестировать API?
Создавать весь фронтенд перед тестированием API – довольно глупое решение. Для этого можно использовать либо “Postman”, либо “Thunder Client”, оба из которых доступны в качестве расширений для VS Code.
Заключение
Flutter и GO – это мощные технологии, которые помогут вам повысить уровень своей игры в области разработки программного обеспечения.
Для просмотра всего исходного кода можно обратиться к этому репозиторию.