The horse plowed the most, but did not become the chairman of the kolkhoz

 
3r3-31. The horse plowed the most, but did not become the chairman of the kolkhoz 3r3758.  
3r3758.  
Recently in the mobile community, you can often hear about Flutter, React Native. It became interesting to me to understand the profit from these pieces. And how much they really change life when developing applications. As a result, 4 (identical in terms of functions performed) applications were created: native Android, native iOS, Flutter, React Native. In this article, I described what I learned from my experience and how similar elements of applications are implemented in the solutions under consideration. 3r3758.  
3r3758.  
Comments: the author of the article is not a professional cross-platform developer. And all that is written about is the look of a novice developer for these platforms. But I think this review will be useful for people who are already using one of the solutions in question, and who are looking to write applications for the two platforms or to improve the process of interaction between iOS and Android. 3r3758.  
3r3758.  
As a developed application, it was decided to make a “Sport Timer”, which will help people involved in sports when performing interval training. 3r3758.  
3r3758.  
The application consists of 3 screens. 3r3758.  
3r3758.  
3r3758.  
Screen timer operation
 
3r3758.  
3r3758.  
Workout history screen 3r3758.  
3r3758.  
3r33412. 3r3758.  
Timer settings screen
 
3r3758.  
This application is interesting to me as a development, because the following components of interest to me will be affected by its creation:
 
- Layout
 
- Custom View
 
- Work with UI lists 3r3758.  
- Multithreading 3r3758.  
- Database 3r3758.  
- Network 3r3758.  
- key-value vault
 
3r3758.  
It is important to note that for Flutter and React Native we can create a bridge (channel) to the native part of the application and use it to implement everything that the operating system provides. But I was wondering what the frameworks give out of the box. 3r3758.  
3r3758.  
3r3752. The choice of tools to develop 3r3753. 3r3758.  
For a native iOS application, I chose the Xcode development environment and the Swift programming language. For native Android, Android Studio and Kotlin. React Native developed in WebStorm, the JS programming language. Flutter - Android Studio and Dart. 3r3758.  
3r3758.  
An interesting fact when developing on Flutter it seemed to me that right from Android Studio (the main IDE for Android development) you can run the application on an iOS device. 3r3758.  
3r3758.  
3r380. 3r3758.  
3r3758.  
3r3752. Project structure 3r3753. 3r3758.  
The structures of native iOS and Android projects are very similar. This is a file for layout with the extensions .storyboard (iOS) and .xml (Android), dependency managers Podfile (iOS) and Gradle (Android), source code files with extensions .swift (iOS) and .kt (Android). 3r3758.  
3r3758.  
3r33939. 3r3758.  
The structure of the project Android
 
3r3758.  
3r3-300. 3r3758.  
The structure of the project is iOS 3r3758.  
3r3758.  
Flutter and React Native structures contain the Android and iOS folders, which contain the usual native projects for Android and iOS. Flutter and React Native are connected to native projects as a library. In fact, when you start Flutter on an iOS device, the normal native iOS application starts with the Flutter library connected. For React Native and for Android, everything is the same. 3r3758.  
3r3758.  
Flutter and React Native also contain package.json (React Native) and pubspec.yaml (Flutter) dependency managers and source code files with the .js (React Native) and .dart (Flutter) extensions that also contain the layout. 3r3758.  
3r3758.  
3r3115. 3r3758.  
The structure of the project Flutter
 
3r3758.  
3r3122. 3r3758.  
The structure of the project React Native
 
3r3758.  
3r3752. Layout
3r3758.  
For native iOS and Android, there are visual editors. This greatly simplifies the creation of screens. 3r3758.  
3r3758.  
3r3758.  
Visual editor for native Android
 
3r3758.  
3r3144. 3r3758.  
Visual editor for native iOS
 
3r3758.  
There are no visual editors for React Native and Flutter, but there is support for the hot-restart feature, which at least simplifies the work with the UI. 3r3758.  
3r3758.  
3r3758.  
Hot reboot in Flutter
 
3r3758.  
3r3162. 3r3758.  
Hot reboot in React Native
 
3r3758.  
On Android and iOS, layout is stored in separate files with the extensions .xml and .storybord, respectively. In React Native and Flutter, the layout comes directly from the code. An important point when describing ui speed is to note that Flutter has its own rendering mechanisms, with which the creators of the framework promise 60 fps. And React Native uses native ui elements that are built using js, which leads to their excessive nesting. 3r3758.  
3r3758.  
In Android and iOS, to change the View property we use a link to it from the code and, for example, to change the background color we cause changes to the object directly. In the case of React Native and Flutter, there is another philosophy: we change the properties inside the setState call, and the view itself is redrawn depending on the changed state. 3r3758.  
3r3758.  
Examples of creating a timer screen for each of the selected solutions: 3r3758.  
3r3758.  
3r3181. 3r3758.  
Screen layout timer on Android
 
3r3758.  
3r3188. 3r3758.  
Screen timer layout on iOS
 
3r3758.  
Screen layout timer Flutter
 
3r33737. @override
Widget build (BuildContext context) {
return Scaffold (
body: Stack (
children:
[
new Container(
color: color,
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
" ${getTextByType(trainingModel.type)}",
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 24.0),
),
new Text(
"${trainingModel.timeSec}",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 56.0),
),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"СЕТЫ ${trainingModel.setCount}",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 24.0),
),
Text(
"ЦИКЛЫ ${trainingModel.cycleCount}",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 24.0),
),
],
),
),
),
padding: const EdgeInsets.all (20.0),
), 3r3771. new Center (
child: CustomPaint (
painter: MyCustomPainter (
//???r3r3771. trainingModel.getPercent ()),
size: Size.infinite,
),
], 3r3771. ))
}
3r33737. 3r3734. 3r3758.  
3r3758.  
Screen layout timer React Native
 
3r33737. render () {3r3771. return (
Seth {this.state.value.setCount}
3r3312.
3r33300.
Cycle {this.state.value.cycleCount}
3r3303.
3r33312.
3r33312.
3r33312.
3r33312.
);
}
3r33737. 3r3734. 3r3758.  
3r3758.  
3r3752. Custom View
3r3758.  
Getting acquainted with the solutions, it was important for me that you could create absolutely any visual component. That is, draw ui at the level of squares, circles and paths. For example, the timer indicator is such a view. 3r3758.  
3r3758.  
3r3758.  
For native iOS, there were no problems, as there is access to the Layer, on which you can draw anything. 3r3758.  
3r33737. let shapeLayer = CAShapeLayer ()
var angle = (-Double.pi /2
- ??? + (Double.pi * 2) * percent)
let circlePath = UIBezierPath (arcCenter: CGPoint (x: 10? y: 100),
radius: CGFloat (95),
startAngle: CGFloat (-Double.pi /2),
endAngle: CGFloat (end.ngle: CGFloat (-Double.pi /2),
endAngle: CGFloat (end.ngle: CGFloat)
Clockwise: true)
shapeLayer.path = circlePath.cgPa
3r33737. 3r3734. 3r3758.  
3r3758.  
For native Android, you can create a class that inherits from View. And override the onDraw (Canvas canvas) method, in the parameter of which the Canvas object is on it and draw. 3r3758.  
3r33737. @Override
protected void onDraw (Canvas canvas) {
pathCircleOne = new Path ();
pathCircleOne.addArc (rectForCircle, -9? value * 3.6F);
canvas.drawPath (pathCircleBackground, paintCircleBackground);
}
3r33737. 3r3734. 3r3758.  
For Flutter, you can create a class that inherits from CustomPainter. And override the paint method (Canvas canvas, Size size), which in the parameter passes the Canvas object - that is, a very similar implementation as in Android. 3r3758.  
3r33737. @override
void paint (Canvas canvas, Size size) {
Path Path = Path ()
addArc (
Rect.fromCircle (
radius: size.width /3.?
center: Offset (size.width /? size.height /2),
),
-pi * 2 /?
(pi * 2 * _percent /100);
canvas.drawPath (path, paint);
}
3r33737. 3r3734. 3r3758.  
3r3758.  
For React Native, no solution was found out of the box. I think this is due to the fact that js is only described by the view, and is built by native ui elements. But you can use the react-native-canvas library, which gives access to the canvas. 3r3758.  
3r33737. handleCanvas = (canvas) => {
if (canvas) {3r3771. var modelTimer = this.state.value;
const context = canvas.getContext ('2d');
context. ;
}
}
3r33737. 3r3734. 3r3758.  
3r3758.  
3r3752. Work with UI lists
3r3758.  
3r33412. 3r3758.  
3r3758.  
The algorithm of work for Android, iOS, Flutter - solutions is very similar. We need to specify how many items are in the list. And output by the item number the cell that you want to draw. 3r3758.  
In iOS, UITableView is used to draw lists in which you need to implement DataSource methods. 3r3758.  
3r33737. func tableView (_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return countCell
}
func tableView (_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return cell
}
3r33737. 3r3734. 3r3758.  
For Android, they use RecyclerView, in whose adapter we implement similar IOS methods. 3r3758.  
3r33737. class MyAdapter (private val myDataset: Array
): 3r3771. RecyclerView.Adapter
() {3r3773. override fun onBindViewHolder (holder: MyViewHolder, position: Int) {
holder.textView.text = myDataset[position]
}
override fun getItemCount () = myDataset.size
}
3r33737. 3r3734. 3r3758.  
For flutter use ListView, in which similar methods are implemented in the builder. 3r3758.  
3r33737. new ListView.builder (
itemCount: getCount () * ?
itemBuilder: (BuildContext context, int i) {
return new HistoryWidget (
Key ("a $ {[index]worktime")) "). models[index]);
},
)
3r33737. 3r3734. 3r3758.  
React Native uses a ListView. The implementation is similar to previous solutions. But there is no binding to the number and number of elements in the list, in the DataSource we set the list of elements. And in the renderRow, we implement the creation of a cell depending on which element has come. 3r3758.  
3r33737. }
/> 3r3773. 3r33737. 3r3734. 3r3758.  
3r3758.  
3r3752. Multithreading 3r3753. 3r3758.  
When I started to deal with multithreading, I was horrified by the variety of solutions. In iOS, this is GCD, Operation, in Android, AsyncTask, Loader, Coroutine, in React Native, Promise, Async /Await, in Flutter-Future, Stream. The principles of some solutions are similar, but the implementation is still different. 3r3758.  
He came to the rescue beloved Rx. If you are not yet in love with him, I advise you to study. It is in all the solutions I consider in the form: RxDart, RxJava, RxJs, RxSwift. 3r3758.  
3r3758.  
RxJava
 
3r33737. Observable.interval (? TimeUnit.SECONDS)
.subscribe (object: Subscriber
() {
fun onCompleted () {
println ("onCompleted")
}
fun onerror (e), which will be useful for you) for 3r3771. 3r3771. + e.message)
}
fun onNext (l: Long?) {
println ("onNext ->" + l !!)
}
}
3r33737. 3r3734. 3r3758.  
3r3758.  
RxSwift
 
3r33737. Observable
.interval (1.? scheduler: MainScheduler.instance)
.subscribe (onNext: {print ($ 0)})
3r33737. 3r3734. 3r3758.  
3r3758.  
RxDart
 
3r33737. Stream.fromIterable ([1, 2, 3])
.transform (new IntervalStreamTransformer (seconds: 1))
.listen ((i) => print ("$ i sec");

 
3r3758.  
RxJS
 
3r33737. Rx.Observable
.interval (500 /* ms * /)
.timeInterval ()
.take (3) 3r3771. .subscribe (3r37737. function (x) {
console.log ('Next:' + x);
},
function (err) {3r3771. console.log ('Error:' + err);
.},
Function () {
Console.log ('Completed');
}) 3r3771. 3r33737. 3r3734. 3r3758.  
As you can see, the code looks very similar in all four languages. That in the future, if necessary, will facilitate your transition from one solution for mobile development to another. 3r3758.  
3r3758.  
3r3752. Database 3r3753. 3r3758.  
In mobile applications, the standard is SQLite database. In each of the solutions considered, a wrapper is written to work with it. Android usually uses ORM Room. 3r3758.  
In iOS, Core Data. In Flutter, you can use the sqflite plugin. 3r3758.  
In React Native - react-native-sqlite-storage. All of these solutions are designed differently. And to make the applications look similar, you will have to write Sqlite queries manually, without using wrappers. 3r3758.  
It is probably better to look towards the Realm data storage library, which uses its own kernel for data storage. It is supported on iOS, Android and React Native. There is currently no support for Flutter, but Realm engineers are working in this direction. 3r3758.  
3r3758.  
Realm in Android
 
3r33737. RealmResults
item = realm.where (Item.class) .lessThan ("id", 2) .findAll ();
3r33737. 3r3734. 3r3758.  
3r3758.  
Realm in iOS 3r3758.  
3r33737. let item = realm.objects (Item.self) .filter ("id < 2")

 
3r3758.  
Realm in React Native
 
3r33737. let item = realm.objects ('Item'). filtered ('id < 2');

 
3r3758.  
3r3752. Key-value storage
3r3758.  
In native iOS, UserDefaults are used. In the native Android - preferences. In React Native and Flutter, you can use libraries that are wrappers for native key-value repositories (SharedPreference (Android) and UserDefaults (iOS)). 3r3758.  
3r3758.  
Android
 
3r33737. SharedPreferences sPref = getPreferences (MODE_PRIVATE);
Editor ed = sPref.edit ();
ed.putString ("my key '", myValue);
ed.commit ();
3r33737. 3r3734. 3r3758.  
3r3758.  
iOS 3r3758.  
3r33737. let defaults = UserDefaults.standard
defaults.integer (forKey: "my key '")
defaults.set (myValue, forKey: "my key")
3r33737. 3r3734. 3r3758.  
3r3758.  
Flutter
 
3r33737. SharedPreferences prefs = await SharedPreferences.getInstance ();
prefs.getInt (my key ')
prefs.setInt (my key ', myValue)
3r33737. 3r3734. 3r3758.  
3r3758.  
React Native
 
3r33737. DefaultPreference.get ('my key'). Then (function (value) {console.log (value)});
DefaultPreference.set ('my key', myValue) .then (function () {console.log ('done')});
3r33737. 3r3734. 3r3758.  
3r3758.  
3r3752. Network 3r3753. 3r3758.  
There are a huge number of solutions for working with the network in native iOS and Android. The most popular are Alamofire (iOS) and Retrofit (Android). React Native and Flutter have written their own platform-independent clients to surf the net. All customers are designed very similarly. 3r3758.  
3r3758.  
Android
 
3r33737. Retrofit.Builder ()
.baseUrl ("https://timerble-8665b.firebaseio.com")
.build ()
@GET ("/messages.json")
fun getData (): Flowable
3r33737. let url = URL (string: "https://timerble-8665b.firebaseio.com/messages.json")
Alamofire.request (url, method: .get)
.responseJSON {response in
3r33737. 3r3734. 3r3758.  
3r3758.  
Flutter
 
3r33737. http.Response response = await
http.get ('https://timerble-8665b.firebaseio.com/messages.json')
3r33737. 3r3734. 3r3758.  
3r3758.  
React Native
 
3r33737. fetch ('https://timerble-8665b.firebaseio.com/messages.json')
.then ((response) => response.json ())
3r33737. 3r3734. 3r3758.  
3r3758.  
3r3752. Speed ​​of development
3r3758.  
3r33737. 3r3758.  
3r3758.  
It is probably incorrect to draw any conclusions based on my development speed, since I am an Android developer. But I think for iOS developer Flutter and Android will seem easier than React Native. 3r3758.  
3r3758.  
3r3752. Conclusion 3r3753. 3r3758.  
Starting to write an article, I knew what I would write in the conclusion. I will tell you which solution you liked more, which solution you should not use. But then, after talking with the people who use these solutions in production, I realized that my conclusions were incorrect, because I look at everything from my own experience. Most importantly, I realized that for each of the solutions under consideration, there are projects for which it is ideal. And sometimes it's really more profitable for a business to do a cross-platform application, rather than plowing over the development of two native ones. And if some decision is not suitable for your project, you should not think that it is bad in principle. I hope the article will be useful. Thanks for getting to the end. 3r3758.  
3r3758.  
Regarding the changes to the article, please write in a personal, I will gladly correct everything. 3r33767.
3r33737. ! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d,! 1): e.attachEvent ("onload", d ): d ()}}} t ("//mediator.mail.ru/script/2820404/"""_mediator") () (); 3r33737.
3r33767.
+ 0 -

Comments 2

Offline
Bruce
Bruce 25 December 2018 14:16
I didn't know about it. Thank you for sharing the information. It's nice to find this post.
instagram online
Offline
Matteo Teagan
Matteo Teagan 8 September 2019 17:34
To be honest I don’t understand the use of content here. I came here to find out about horse shampoo, and not this. Though I am not surprised that I stumbbled upin other content.

Add comment