תחי ישראל - אין לנו ארץ אחרת

תחי ישראל -אין לנו ארץ אחרת

ניהול משתמשים ב-Flutter - מדריך # 3 - עדכון משתמש קיים

 

מחבר:
בתאריך:

זהו המדריך השלישי בסדרת המדריכים המוקדשת לניהול משתמשים באפליקציית Flutter. במדריך הראשון למדנו כיצד לרשום משתמש לאפליקציה, בשני לעשות login למשתמש קיים. במדריך זה נוסיף את הקוד המאפשר למשתמש לערוך את פרטיו ולהעלות תמונה.

 

להורדת הקוד - אפליקציית Flutter לניהול משתמשים על גבי פלטפורמת Firebase

 

Update user screen in a Flutter app with Firebase backend in action

 

קלאס Provider לעדכון המשתמש

נוסיף לקלאס לניהול המשתמשים (ראה מדריך ראשון בסדרה על ניהול משתמשים באפליקציית Flutter) פונקציות להבאת מידע על המשתמש הנוכחי הרשום לאפליקציה:

lib/providers/auth_provider.dart

User? get currentUser => _firebaseAuth.currentUser;


Stream<User?> get authStateChanges => _firebaseAuth.authStateChanges();

נוסיף ל-provider קוד להבאת המידע על משתמש על פי ה-id שלו:

lib/providers/auth_provider.dart

Future<AppUserData> getByUid(String userId) async => _usersCollection
         .doc(userId)
         .get()
         .then((DocumentSnapshot documentSnapshot) {
       final data = documentSnapshot.data();
       final AppUserData userData =
           AppUserData.fromJson(data as Map<String, dynamic>);
       return userData;
     });
  • הפונקציה מקבלת את הפרמטר id מזהה משתמש
  • הפונקציה קוראת ממסד הנתונים של Firebase
  • הפונקציה מתרגמת את הנתונים המוחזרים מפורמט JSON לאובייקט AppUserData.

הפונקציה מחזירה אובייקט מסוג AppUserData ולפיכך לא נשכח לייבא את הקלאס המגדיר את האובייקט:

lib/providers/auth_provider.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';


import '../models/app_user_model.dart';

נוסיף ל-provider פונקציה נוספת לעדכון המשתמש:

lib/providers/auth_provider.dart

Future<bool> updateUserData(String userId, String email,
     {String profilePic = "",
     String userName = "",
     List<String> carsKeys = const []}) async {
   try {
     final Map<String, dynamic> data = {
       "uid": userId,
       "email": email,
       "name": userName,
       "profile_pic": profilePic,
       "cars": carsKeys,
       "updated_at": DateTime.now().millisecondsSinceEpoch,
     };


     await _usersCollection.doc(userId).set(data);
     return true;
   } catch (e) {
     print("error = ${e.toString()}");


     return false;
   }
}
  • הפונקציה מקבלת פרמטרים: תמונת משתמש, מייל, שם, רשימת המכוניות של המשתמש.
  • הפונקציה יוצרת מפה Map אותה היא מזינה למסד הנתונים Firebase.

עוד נחזור לקידוד רשימת המכוניות של המשתמש אבל קודם נטפל במסך המאפשר למשתמש לעדכן את פרטיו.

 

מסך עדכון פרטי המשתמש

נוסיף לתיקיית screens של הפרויקט את הקובץ שייצר את מסך עדכון פרטי המשתמש:

lib/screens/update_user_screen.dart

לתוך הקובץ החדש נוסיף stateful widget בשם UpdateUserScreen :

lib/screens/update_user_screen.dart

import 'package:flutter/material.dart';


class UpdateUserScreen extends StatefulWidget {
 const UpdateUserScreen({super.key});


 @override
 State<UpdateUserScreen> createState() => _UpdateUserScreenState();
}


class _UpdateUserScreenState extends State<UpdateUserScreen> {
 @override
 Widget build(BuildContext context) {
   return Container();
 }
}

את התמונה שיעלה המשתמש אנחנו רוצים לשמור בענן. נתקין את החבילה: firbase_storage שתעשה בשבילנו את רוב העבודה. לשם כך, נפתח את הטרמינל בתוך תיקיית השורש של האפליקציה, ונריץ את הפקודה הבאה כדי להתקין את החבילה:

$ flutter pub add firebase_storage

נייבא את הספריות הדרושות לניהול המשתמש:

lib/screens/update_user_screen.dart

import 'package:flutter/material.dart';


import 'package:provider/provider.dart';


import 'package:firebase_storage/firebase_storage.dart';
import 'package:firebase_auth/firebase_auth.dart';


import '../models/app_user_model.dart';
import '../providers/auth_provider.dart';
  • Provider - לניהול והעברת המידע. לדוגמה, בין המסך לקלאס שמכיל את הפונקציות לניהול המשתמש.
  • חבילות של Firebase בשביל לעבוד עם שירות ניהול המשתמשים FirebaeAuth וכדי שנוכל להעלות את תמונת המשתמש לאחסון בענן.
  • app_user_model ו- auth_provider בשביל הגדרת וניהול המידע הדרוש לנו לניהול משתמשים

כדי שנוכל לראות את המסך שהוספנו נשבץ אותו ב-widgetTree במסגרת החלופה המיועדת למשתמש המזוהה על ידי האפליקציה כרשום למערכת:

lib/widgets/widget_tree.dart

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';


import '../screens/auth_screen.dart';
import '../screens/update_user_screen.dart';


class WidgetTree extends StatefulWidget {
 const WidgetTree({super.key});


 @override
 State createState() => _WidgetTreeState();
}


class _WidgetTreeState extends State {
 @override
 Widget build(BuildContext context) {
   return StreamBuilder(
       stream: FirebaseAuth.instance.authStateChanges(),
       builder: (context, snapshot) {
         if (snapshot.connectionState == ConnectionState.waiting) {
           return const Center(
             child: CircularProgressIndicator(),
           );
         } else if (snapshot.hasError) {
           return const Center(
             child: Text("Error in auth"),
           );
         } else if (snapshot.hasData) {
           return const UpdateUserScreen();
         } else {
           return const AuthScreen();
         }
       });
 }
}

אנחנו מעוניינים לקבל את המידע על המשתמש, ולהציג אותו בטופס שהמשתמש יכול לערוך.

נגדיר בתוך הקלאס את קוד הקונטרולרים אשר בטופס:

lib/screens/update_user_screen.dart

final _formKey = GlobalKey<FormState>();
final TextEditingController _nameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();

נגדיר את המשתנים המכילים את המידע אודות המשתמש :

lib/screens/update_user_screen.dart

var userUid = "";
var userImageUrl = "";
var userEmail = "";
var userName = "";


List<String> selectedCars = [];


var emailVerified = false;
  • לבד משם המשתמש והסיסמה, פרטי המשתמש עשויים לכלול תמונה שהוא העלה המאוחסנת בענן ורשימה של מכוניות שהוא בחר להוסיף לרשימת מכוניות.

נמשוך את המידע על המשתמש באמצעות פונקציה פרטית setUserData המביאה את המידע משירות FirebaseAuth:

lib/screens/update_user_screen.dart

final User? user = AuthProvider(FirebaseAuth.instance).currentUser;


void _setUserData() async {
   if (user != null) {
     userName = user!.displayName ?? '';
     userEmail = user!.email as String;
     userImageUrl = user!.photoURL ?? '';
     userUid = user!.uid;


     // Check if user's email is verified
     emailVerified = user!.emailVerified;


     _nameController.text = userName;


     var auth = AuthProvider(FirebaseAuth.instance);
     var byUid = auth.getByUid(userUid);
     AppUserData userData = await byUid;
     selectedCars = userData.cars;


     setState(() {});
   }
 }

נייבא את התלויות:

lib/screens/update_user_screen.dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';


import '../models/app_user_model.dart';
import '../providers/auth_provider.dart';

נקרא לפונקציה setUserData עם יסוד הווידג'ט מתוך המתודה initState כדי שתביא ותזין את הטופס בפרטיו של המשתמש:

lib/screens/update_user_screen.dart

@override
void initState() {
   _setUserData();
   super.initState();
}

המשתנים הבאים ישמשו בהמשך להצגת הודעות חיווי למשתמש:

lib/screens/update_user_screen.dart

var alertText = 'Enter name and/or password';
var alertColor = Colors.blue;

 

העלאת תמונת המשתמש

עדיין במסך עדכון פרטי המשתמש נרצה לאפשר למשתמש לצלם תמונה בנייד שלו ולהעלות לאחסון בענן.

נתקין את החבילה image_picker בשביל העלאת תמונות המשתמש באמצעות הרצת הפקודה הבאה בטרמינל:

$ flutter pub add image_picker

נוסיף את הספריות הדרושות לנו:

lib/screens/update_user_screen.dart

import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:image_picker/image_picker.dart';
  • dart:io - מאפשר עבודה עם קבצים, תיקיות, סוקטים, שרתי HTTP וקליינטים.
  • firebase_storage - בשביל לעבוד עם אחסון הענן של Firebase.
  • בספרייה image_picker אנחנו משתמשים כדי ללכוד תמונות שהמשתמש מצלם עם המכשיר שלו.

נוסיף 2 משתנים בשביל בחירת התמונות:

lib/screens/update_user_screen.dart

final picker = ImagePicker();
var imageUploaded = false;

הפונקציה pickImage פותחת את המצלמה על המכשיר הנייד, ומחכה לתמונה שיצלם המשתמש. אח"כ היא מעלה את התמונה לאחסון Firebase:

lib/screens/update_user_screen.dart

Future<void> pickImage() async {
   try {
     final pickedFile = await picker.pickImage(
         source: ImageSource.camera, maxWidth: 500, maxHeight: 500);
     String path = "images/${user!.uid.toString()}";
     Reference ref = FirebaseStorage.instance.ref().child(path);


     await ref.putFile(File(pickedFile!.path));
     ref.getDownloadURL().then((value) {
       setState(() {
         userImageUrl = value;
         imageUploaded = true;


         alertColor = Colors.orange;
         alertText =
             'Image uploaded. To save the image to your profile press the "Update profile" button';
       });
     });
   } catch (e) {
     setState(() {
       imageUploaded = false;


       alertColor = Colors.red;
       alertText = e.toString();
     });
   }
 }
  • במקרה של הצלחה הפונקציה לא מחזירה דבר, void. במקרה של בעיה היא תעדכן הודעת שגיאה אשר תוצג למשתמש.

 

עדכון רשימת המכוניות מצד ה-provider

עד כה ראינו שדות די סטנדרטיים: שם, סיסמה, תמונה. בנוסף, נאפשר למשתמש לבחור מספר מכוניות כדי שנלמד לעבוד עם רשימה.

את העבודה עם מסד הנתונים ננהל באמצעות קלאס מסוג Provider בדומה למה שהסברתי בהרחבה במדריך עבודה עם Provider באפליקציית Flutter.

נפתח קובץ חדש בשביל ה-cars_provider בתיקיית providers:

providers/cars_provider.dart

import 'dart:async';


import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';


class Car {
 String key;
 String name;


 Car({required this.key, required this.name});
}
  • הקלאס Car מגדיר את השדות הדרושים להגדרת מכונית באפליקציה: שם ומזהה ייחודי, key.

נוסיף קלאס ה-CarsProvider שמרחיב את ChangeNotifier מה שהופך אותו לספק מידע בדפוס provider:

providers/cars_provider.dart

class CarsProvider extends ChangeNotifier {
}

הקלאס מאזין בזמן אמת real-time לזרם מידע stream שמקורו בקולקציה cars שנמצאת במסד הנתונים של Firebase בענן:

providers/cars_provider.dart

class CarsProvider extends ChangeNotifier {
 final CollectionReference _carsCollection =
     FirebaseFirestore.instance.collection("cars");


 late StreamSubscription<dynamic> _carsStream;


 // ignore: prefer_final_fields
 List<Car> _cars = [];


 List<Car> get cars => List.unmodifiable(_cars);


 CarsProvider() {
   _listenToCars();
 }


 void _listenToCars() async {
   try {
     _carsStream = _carsCollection
         .limit(10)
         .orderBy('name', descending: true)
         .snapshots()
         .listen((querySnapshot) {
       for (var doc in querySnapshot.docs) {
         final data = doc.data() as Map<String, dynamic>;
         _cars.add(Car(key: doc.id, name: data["name"]));
       }


       notifyListeners();
     });
   } catch (err) {
     print("$err");
   }
 }


 @override
 void dispose() {
   _carsStream.cancel();
   super.dispose();
 }
}
  • הפונקציה listenToCars מאזינה לזרם המידע, ורושמת אותו למערך הפרטי cars.
  • פונקצית getter ששמה cars מספקת את המערך לנרשמים לזרם המידע.
  • ההרשמה לזרם המידע נעשית עם ייסוד האובייקט מהקלאס על ידי קריאה לפונקציה listenToCars מתוך פונקצית האיניציאציה ששמה כשם הקלאס CarsProvider.
  • הפסקת זרם המידע חיונית למניעת memory leakage כשהאובייקט שיצרנו מהקלאס יוצא מכלל פעולה. נבטל את ההרשמה לזרם המידע בתוך המתודה dispose של ה-life cycle.

לתוך הקלאס CarsProvider נוסיף מתודה שתאפשר למשתמש להוסיף מכוניות:

providers/cars_provider.dart

Future<bool> add(String name) async {
   try {
     final Map<String, dynamic> data = {
       "name": name.toString(),
       "date": DateTime.now().millisecondsSinceEpoch,
     };
     await _carsCollection.add(data);
     return true;
   } catch (err) {
     return false;
   }
 }
  • הפונקציה דורשת את שם המכונית ומוסיפה את התאריך העכשווי בעצמה. שני שדות אלו הם כל מה שדרוש להגדרת אובייקט מסוג Car באפליקציה.

נרשום את ה-provider כספק מידע לאפליקציה כולה מתחת ל- AuthProvider בסקריפט הראשי של האפליקציה:

lib/main.dart

@override
Widget build(BuildContext context) {
   return MultiProvider(
     providers: [
       ListenableProvider(
         create: (_) => AuthProvider(FirebaseAuth.instance),
       ),
       ChangeNotifierProvider(
         create: (context) => CarsProvider(),
       ),
     ],
     child: MaterialApp(
       title: 'Auth app',
       debugShowCheckedModeBanner: false,
       theme: ThemeData(
         primaryColor: Colors.blue,
         scaffoldBackgroundColor: Colors.white,
       ),
       home: const WidgetTree(),
     ),
   );
}

 

עדכון רשימת המכוניות מצד ממשק המשתמש

התקשורת בין המסך לעדכון פרטי המשתמש לבין הקלאס Provider נעשית על פי דפוס שבו יש צורך להרשם לזרם המידע כדי להשתמש בו.

נייבא את התלויות לטופס ניהול המשתמש:

lib/screens/update_user_screen.dart

import 'package:provider/provider.dart';
import '../providers/cars_provider.dart';

נוסיף ווידג'ט carsSelector שיהווה ממשק משתמש כאשר המשתמש בוחר בלחיצה איזה מכונית להוסיף לרשימה שלו, ואיזה להסיר. הווידג'ט קולט את רשימת המכוניות כזרם מידע שאליו הוא נרשם כ-Consumer כאשר הוא מייצר מכל אחד מפריטי המידע ווידג'ט GestureDetector המציג את שם המכונית כאשר לחיצה אחראית להוספת או להסרת הפריט ממערך selectedCars המכיל את רשימת המכוניות הנבחרות של המשתמש:

lib/screens/update_user_screen.dart

Widget carsSelector() {
   return Consumer<CarsProvider>(
     builder: ((context, model, child) {
       // the widget to rebuild whenever notifyListeners fires


       List<Widget> children = model.cars.map(
         (car) {
           return Container(
             decoration: BoxDecoration(
               borderRadius: BorderRadius.circular(4),
               color:
                   selectedCars.contains(car.key) ? Colors.red : Colors.blue,
             ),
             padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
             child: GestureDetector(
               onTap: () {
                 setState(() {
                   if (selectedCars.contains(car.key)) {
                     selectedCars.remove(car.key);
                   } else {
                     selectedCars.add(car.key);
                   }
                 });
               },
               child: Text(
                 car.name,
                 style: const TextStyle(
                   fontSize: 16,
                 ),
               ),
             ),
           );
         },
       ).toList();
       return Align(
         alignment: Alignment.centerLeft,
         child: Wrap(
           spacing: 10,
           runSpacing: 10,
           children: [
             const Text("Your cars: "),
             ...children,
           ],
         ),
       );
     }),
   );
 }
  • בווידג'ט לניהול רשימת המכוניות נשתמש בתוך הטופס כאשר נוסיף אותו.

 

פונקציה לעדכון פרטי המשתמש

את הפרטים בטופס נשלח לפונקציה לעדכון המידע אודות המשתמש:

lib/screens/update_user_screen.dart

Future<bool> updateAuth() async {
   try {
     if (_nameController.text.trim().length > 2) {
       setState(() {
         userName = _nameController.text.trim();
       });
       await user?.updateDisplayName(userName);
     }


     if (_passwordController.text.trim().length > 5) {
       await user?.updatePassword(_passwordController.text.trim());
     }


     if (imageUploaded && userImageUrl != '') {
       await user?.updatePhotoURL(userImageUrl);
     }


     setState(() {
       alertColor = Colors.green;
       alertText = 'User profile updated successfully';
     });
     return true;
   } on FirebaseAuthException catch (e) {
     alertColor = Colors.red;
     if (e.message.runtimeType == String) {
       alertText = e.message!;
     } else {
       alertText = 'Something went wrong!';
     }
     return false;
   }
 }
  • הפונקציה מעדכנת את פרטי המשתמש בענן.
  • הפונקציה מחזירה תוצא בוליאני. במקרה של הצלחה היא מחזירה true . אחרת false + הודעת שגיאה.

 

האלמנטים של הטופס Form

נחליף את הווידג'ט Container ב-Scaffold הכולל בתוכו Form:

lib/screens/update_user_screen.dart

@override
Widget build(BuildContext context) {
   final tm = Theme.of(context);
   return Scaffold(
     appBar: AppBar(
       backgroundColor: tm.primaryColor,
       centerTitle: true,
       title: Text("Update user profile"),
     ),
     body: Container(
       height: double.infinity,
       width: double.infinity,
       padding: const EdgeInsets.all(20),
       child: Form(
     




       ),
     ),
   );
 }
  • אנחנו משתמשים פה ב-
final tm = Theme.of(context);

בשביל למשוך את הגדרות את העיצוב theming מההורים. במקרה זה, נקבל את ה-ThemeData המוגדרים בווידג'ט ההורה של כל האפליקציה בקובץ main.dart. נשתמש בזה כדי להגדיר את צבע הרקע של ה-AppBar.

נשבץ לתוך הטופס Form את השדות הדרושים לנו אחד מעל לשני בתוך ווידג'ט Column:

lib/screens/update_user_screen.dart

Form(
  key: _formKey,
  child: SingleChildScrollView(
    child: Column(
      children: [
        alert(alertText, alertColor),
        const SizedBox(height: 20),
        TextFormField(
                 controller: _nameController,
                 decoration: textInputDecoration.copyWith(
                   labelText: "Name",
                 ),
                 validator: (String? value) {
                   return (value != null && value.length >= 2)
                       ? null
                       : 'At least 2 characters';
                 },
        ),
        const SizedBox(height: 10),
        carsSelector(),
        const SizedBox(height: 10),
               TextFormField(
                 controller: _passwordController,
                 decoration: textInputDecoration.copyWith(
                   labelText: "Password",
                 ),
                 obscureText: true,
                 validator: (String? value) {
                   return (value != null && value.length >= 6)
                       ? null
                       : 'At least 6 characters';
                 },
        ),
        const SizedBox(height: 20),
        GestureDetector(
                 onTap: () {
                   pickImage();
                 },
                 child: Container(
                   color: Colors.blue.shade600,
                   padding: const EdgeInsets.all(8),
                   child: const Text('Upload image',
                       style: TextStyle(color: Colors.white)),
                 ),
        ),
        const SizedBox(height: 10),
        profileImage(userImageUrl),
        const SizedBox(height: 20),
        ElevatedButton.icon(
                 onPressed: () async => updateAuth().then(
                   (success) {
                     if (success) {
                       Provider.of<AuthProvider>(context, listen: false)
                           .updateUserData(
                         userUid,
                         userEmail,
                         carsKeys: selectedCars,
                       );


                       Future.delayed(
                         const Duration(seconds: 3),
                         () {
                           Navigator.pop(context);
                           displaySnackbar(
                               context,
                               "Profile updated successfully",
                               Colors.green,
                               Colors.white);
                         },
                       );
                     }
                   },
                 ),
                 icon: const Icon(Icons.mode_edit),
                 label: const Text("Update profile",
                     style: TextStyle(fontSize: 20)),
                 style: ElevatedButton.styleFrom(
                   minimumSize: const Size.fromHeight(50),
                 ),
               )
             ],
           ),
         ),
),
  • הformKey מזהה את הטופס. נעזר בו בשביל לעשות ולידציה של שדות הטופס.
  • את הווידג'ט מסוג Column נעטוף בתוך SingleChildScrollView בשביל שאפשר יהיה לגלול את הטופס אנכית, למקרה שהוא ארוך מהמסך.
  • השדה הראשון ב-Colum הוא alert המציג הודעות למשתמש. זה ווידג'ט שנוסיף את הקוד שלו בהמשך המדריך.
  • שדה טופס Name יאפשר למשתמש לערוך את שם המשתמש.
  • ווידגט carsSelector אותו הוספנו לעיל משמש כ-UI לבחירת פריטים מתוך רשימה (של מכוניות).
  • שדה password בשביל עריכת הסיסמה.
  • ווידג'ט GestureDetector שלחיצה עליו מפעילה פונקציה להעלאה של תמונה.
  • ווידג'ט profileImage שתיכף נוסיף מציג את תמונת המשתמש.
  • כפתור שליחה מסוג ElevatedButton כדי שאפשר יהיה לשלוח את הטופס.
  • במידה ותהליך העדכון הסתיים בהצלחה פונקציה displaySnackbar תציג למשתמש חיווי. תכף נראה את הקוד שלה.

הווידג'ט alert משמש להצגת הודעות למשתמש. הוא מקבל פרמטרים צבע אותיות וטקסט הודעה:

Widget alert(String txt, Color color) {
 return Container(
   padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
   width: double.infinity,
   decoration: BoxDecoration(color: color),
   child: RichText(
     text: TextSpan(
       text: txt,
       style: const TextStyle(
         color: Colors.white,
         fontWeight: FontWeight.bold,
       ),
     ),
   ),
 );
}

הווידג'ט profileImage יציג תמונה של המשתמש רק במידה ומידע זה מועבר לו כפרמטר:

Widget profileImage(userImageUrl) {
 return Center(
   child: userImageUrl == ""
       ? Icon(Icons.person_add, size: 120, color: Colors.blue)
       : Container(
           alignment: Alignment.center,
           width: double.infinity,
           height: 200,
           decoration: BoxDecoration(
             borderRadius: BorderRadius.circular(10),
             image: DecorationImage(
               image: NetworkImage(userImageUrl),
               fit: BoxFit.cover,
             ),
           ),
         ),
 );
}

הפונקציה displaySnackbar מציגה SnackBar, על סמך הפרמטרים שהיא מקבלת: צבע, טקסט ומשך ההצגה.

displaySnackbar(context, message, Color background, Color color,
   {action, duration = 3}) {
 return ScaffoldMessenger.of(context).showSnackBar(
   SnackBar(
       content: Text(message, style: TextStyle(color: color)),
       duration: Duration(seconds: duration),
       backgroundColor: background,
       action: action),
 );
}

 

מדריכים נוספים בסדרת ה-Flutter שעשויים לעניין אותך

 

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.

למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.

שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.

המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?

השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.

הוסף תגובה חדשה

 

 

ענה על השאלה הפשוטה הבאה כתנאי להוספת תגובה:

דג למים הוא כמו ציפור ל...?